当参数类型为 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
函数。
- 具有
input_t = int
的模板参数应导致 void Input(int i)
- 具有
input_t = std::vector
的模板参数应导致 void Input(const std::vector& i)
- 具有
input_t = void
的模板参数应删除 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 后立即将其视为您的成员函数的错误。使用默认模板参数正是我们需要的技巧。我们要测试的现在被认为是依赖的。
我有一个模板 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
函数。
- 具有
input_t = int
的模板参数应导致void Input(int i)
- 具有
input_t = std::vector
的模板参数应导致void Input(const std::vector& i)
- 具有
input_t = void
的模板参数应删除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 后立即将其视为您的成员函数的错误。使用默认模板参数正是我们需要的技巧。我们要测试的现在被认为是依赖的。