如何使用sfinae选择构造函数?

Wal*_*ter 25 c++ constructor sfinae c++11

在模板元编程中,可以在返回类型上使用SFINAE 来选择某个模板成员函数,即

template<int N> struct A {
  int sum() const noexcept
  { return _sum<N-1>(); }
private:
  int _data[N];
  template<int I> typename std::enable_if< I,int>::type _sum() const noexcept
  { return _sum<I-1>() + _data[I]; }
  template<int I> typename std::enable_if<!I,int>::type _sum() const noexcept
  { return _data[I]; }
};
Run Code Online (Sandbox Code Playgroud)

但是,这对构造函数不起作用.假设,我想声明构造函数

template<int N> struct A {
   /* ... */
   template<int otherN>
   explicit(A<otherN> const&); // only sensible if otherN >= N
};
Run Code Online (Sandbox Code Playgroud)

但不允许这样做otherN < N.

那么,SFINAE可以在这里使用吗?我只对允许自动模板参数扣除的解决方案感兴趣

A<4> a4{};
A<5> a5{};
A<6> a6{a4};  // doesn't compile
A<3> a3{a5};  // compiles and automatically finds the correct constructor
Run Code Online (Sandbox Code Playgroud)

注意:这是一个非常简单的例子,其中SFINAE可能过度并且static_assert可能就足够了.但是,我想知道我是否可以使用SFINAE.

Ker*_* SB 28

您可以向模板添加默认类型参数:

template <int otherN, typename = typename std::enable_if<otherN >= N>::type>
explicit A(A<otherN> const &);
Run Code Online (Sandbox Code Playgroud)

  • 请注意,如果您有两个条件不同的此类构造函数,则此方法将无法正常工作。(如果您没有重载,则可能应该使用static_assert,但已经在问题中进行了处理) (2认同)

Emi*_*lia 9

触发SFINAE的方法有很多,enable_if只是其中一种。首先:

Wats是std :: enable_if吗?

就是这样:

template<bool, class T=void> enable_if{ typedef T type; };
template<class T> enable_if<false,T> {};
template<bool b, class T=void> using enable_if_t = typename enable_f<b,T>::type;
Run Code Online (Sandbox Code Playgroud)

这个想法是使typename enable_if<false>::type成为一个错误,因此使包含它的任何模板声明都被跳过。

那么如何选择触发功能呢?

禁用功能

这个想法使声明在某种程度上是错误的:

按退货类型

template<class Type>
std::enable_if_t<cond<Type>::value,Return_type> function(Type);
Run Code Online (Sandbox Code Playgroud)

通过实际参数

template<class Type>
return_type function(Type param, std::enable_if_t<cond<Type>::value,int> =0) 
Run Code Online (Sandbox Code Playgroud)

通过模板参数

template<class Type, 
    std::enable_if_t<cond<Type>::value,int> =0> //note the space between > and =
return_type function(Type param) 
Run Code Online (Sandbox Code Playgroud)

选择功能

您可以使用以下技巧来参数化不同的替代方案:

tempplate<int N> struct ord: ord<N-1>{};
struct ord<0> {};

template<class T, std::enable_if<condition3, int> =0>
retval func(ord<3>, T param) { ... }

template<class T, std::enable_if<condition2, int> =0>
retval func(ord<2>, T param) { ... }

template<class T, std::enable_if<condition1, int> =0>
retval func(ord<1>, T param) { ... }

template<class T> // default one
retval func(ord<0>, T param) { ... }

// THIS WILL BE THE FUCNTION YOU'LL CALL
template<class T>
retval func(T param) { return func(ord<9>{},param); } //any "more than 3 value"
Run Code Online (Sandbox Code Playgroud)

如果满足,将调用第一个/第二个/第三个/第四个函数condition3condition2condition1不是没有一个。

其他SFINAE触发器

编写编译时条件可以是显式的专业问题,也可以是表达式的成功/失败未评估的问题:

例如:

template<class T, class = void>
struct is_vector: std::false_type {};
template<class X>
struct is_vector<vector<X> >:: std::true_type {};
Run Code Online (Sandbox Code Playgroud)

使is_vector<int>::valuefalse不过is_vecttor<vector<int> >::valuetrue

或者,通过内省,例如

template<class T>
struct is_container<class T, class = void>: std::false_type {};

template<class T>
struct is_container<T, decltype(
  std::begin(std::declval<T>()),
  std::end(std::declval<T>()),
  std::size(std::declval<T>()),
  void(0))>: std::true_type {};
Run Code Online (Sandbox Code Playgroud)

这样is_container<X>::value就可以trueX x,您可以编译std::begin(x)等。

诀窍在于,只有所有子表达式都是可编译的,the decltype(...)实际上是void,运算符将丢弃先前的表达式)。


甚至可以有许多其他选择。希望在这一切之间您能找到有用的东西。

  • 这个答案比公认的要好得多。 (5认同)

Pau*_* II 8

在C++ 11中,您可以使用默认模板参数:

template <int otherN, class = typename std::enable_if<otherN >= N>::type>
explicit A(A<otherN> const &);
Run Code Online (Sandbox Code Playgroud)

但是,如果您的编译器还不支持默认模板参数,或者您需要多次重载,那么您可以使用这样的默认函数参数:

template <int otherN>
explicit A(A<otherN> const &, typename std::enable_if<otherN >= N>::type* = 0);
Run Code Online (Sandbox Code Playgroud)


max*_*x66 6

可接受的答案在大多数情况下是好的,但如果存在两个具有不同条件的此类构造函数重载,则失败。在这种情况下,我也在寻找解决方案。

是:可接受的解决方案有效,但不适用于两个替代构造函数,例如,

template <int otherN, typename = typename std::enable_if<otherN == 1>::type>
explicit A(A<otherN> const &);

template <int otherN, typename = typename std::enable_if<otherN != 1>::type>
explicit A(A<otherN> const &);
Run Code Online (Sandbox Code Playgroud)

因为,如本页中所述

一个常见的错误是声明两个函数模板,它们的默认模板参数仅不同。这是非法的,因为默认模板参数不是功能模板签名的一部分,并且声明两个具有相同签名的不同功能模板是非法的。

如同一页中所建议,您可以通过将SFINAE修改签名,应用于值(而非类型)模板参数的类型来解决此问题,如下所示

template <int otherN, typename std::enable_if<otherN == 1, bool>::type = true>
explicit A(A<otherN> const &);

template <int otherN, typename std::enable_if<otherN != 1, bool>::type = true>
explicit A(A<otherN> const &);
Run Code Online (Sandbox Code Playgroud)