当参数类型为 void 时禁用函数

Disable Function when parameter type is void

我有一个模板 class 看起来像这样:

template <typename T> constexpr bool is_value_passable_v = is_trivially_copyable_v<T> && sizeof(T) <= sizeof(void*) && !is_polymorphic_v<T>;
template <typename B, typename T> using param_base_t = conditional_t<is_value_passable_v<B>, T, const T&>;

template <typename T> struct param_d
{
    using type = param_base_t<T, T>;
};

template <> struct param_d<void>
{
    using type = void;
};

template <typename T> using param_t = typename param_d<T>::type;

template <class TIn> class CClass
{
    public:
        static constexpr bool use_input_v = !is_same_v<typename TIn::input_t, void>;
        using input_t = conditional_t<use_input_v, param_t<typename TIn::input_t>, void>;

        enable_if_t<use_input_v> Input(input_t i);
};

此代码的目标是为不同的模板参数提供不同的 Input 函数。

用 clang 编译得到

/usr/bin/../include/c++/v1/type_traits:225:78: error: no type named 'type' in 'std::__1::enable_if<false, void>'; 'enable_if' cannot be used to disable this declaration
template <bool _Bp, class _Tp = void> using enable_if_t = typename enable_if<_Bp, _Tp>::type;
                                                                             ^~~

编辑 1: 添加行后

template <typename T> static constexpr bool use_input2_v = use_input_v;

并将函数声明替换为

template <typename T = void> enable_if_t<use_input2_v<T>> Input(input_t i)

clang 抱怨有 no matching member function for call to 'Input':

note: candidate template ignored: substitution failure [with T = void]: non-type template argument is not a constant expression
template <typename T = void> enable_if_t<use_input2_v<T>> Input(input_t i);
                                         ~~~~~~~~~~~~     ^

编辑 2: 忘了说,这个错误伴随着模板参数的所有三种变体。

编辑 3: CClass 的示例用例可以是

class CInput0
{
    using input_t = int;
};

class CInput1
{
    using input_t = std::vector<int>;
};

class CInput2
{
    using input_t = void;
};

CClass<CInput0> in0;
CClass<CInput1> in1;
CClass<CInput2> in2;

std::vector<int> i = {1, 2, 3};

in0.Input(3);
in1.Input(i);
//in2.Input() disabled

如果要启用基于模板参数属性的函数,无需部分特化,您可以使用以下模式:

#include <iostream>
#include <type_traits>
#include <vector>

std::ostream& operator<<(std::ostream& out, std::vector<int>& value);

template <class TIn> class CClass
{
    public:         
        template <class T =TIn, class PARAM=  std::enable_if_t<!(std::is_same<void,typename T::input_t>::value) ,typename T::input_t > >
        void Input(PARAM i){
            std::cout << "Called Input with parameter: "<< i << std::endl;
        }
};

struct CInput0{ using input_t = int;};
struct CInput1{ using input_t = std::vector<int>;};
struct CInput2{ using input_t = void;};

CClass<CInput0> in0;
CClass<CInput1> in1;
CClass<CInput2> in2;

std::vector<int> i = {1, 2, 3};
std::ostream& operator<<(std::ostream& out, std::vector<int>& value) {
    for (auto& e:value) {
        out << e;
    } 
    out << std::endl;
    return out;
}

int main() {

    in0.Input(3);
    in1.Input(i);
    //in2.Input() disabled
}

这是您的示例的简化版本,您应该能够对其进行调整。

要使 SFINAE 正常工作,它需要处理依赖类型,否则不会出现替换失败。这是一个例子:

    template <typename Self = CClass<TIn>>
    typename std::enable_if<Self::use_input_v>::type
      Input(typename Self::input_t) { }

当成员函数是模板时,编译器会根据模板参数是否有效来有条件地创建它。在您的原始示例中,由于整个 class 是一个模板,但方法不是,编译器会在实例化 class 后立即将其视为您的成员函数的错误。使用默认模板参数正是我们需要的技巧。我们要测试的现在被认为是依赖的。