SFINAE条件和构造函数参数类型

时间:2016-09-07 10:15:59

标签: c++ templates c++11 constructor sfinae

我遇到了以下允许在T周围构建包装器对象的技术,但是从U类型的对象构建,如果T可以从U构造:

template< typename T >
struct S {
    template< typename U,
              typename = std::enable_if_t<
                  std::is_constructible< T, U >::value > >
    explicit S (U&& arg) : value (arg)
        { }
    ...
};

IIUC,U测试中使用的is_constructible类型可能与arg的cv限定类型不同。
虽然表达式value(arg)有效,但SFINAE测试是否可能失败?

1 个答案:

答案 0 :(得分:4)

  

虽然表达式值(arg)有效,但SFINAE测试是否可能失败?

你写S的方式:是的,这是可能的。
它遵循一个最小的(非)工作示例:

#include<type_traits>

template< typename T >
struct S {
    template< typename U
              , typename = std::enable_if_t<std::is_constructible< T, U >::value >
    > explicit S (U&& arg) : value{arg} {}

    T value;
};

struct A {};
struct B {
    B(A &) {}
};

int main() {
    S<B> s(A{});
}

上面的代码不能编译,但如果您注释掉以下行,它会编译:

, typename = std::enable_if_t<std::is_constructible< T, U >::value >

问题是您没有转发构造函数参数。相反,您正在使用对Aarg)的rvalue引用类型的变量的左值引用作为value的参数。
B不能从对A的右值引用构造,并且sfinae表达式失败(正确),但实际上并没有使用这样的引用来构造参数,因此删除了它所使用的sfinae表达式。登记/> 实际上,B可以从A的左值引用构造,这就是您在编写value{arg}时使用的内容。

你应该写下S,如下所示:

#include<utility>

// ...

template< typename T >
struct S {
    template< typename U
              , typename = std::enable_if_t<std::is_constructible< T, U >::value >
    > explicit S (U&& arg) : value (std::forward<U>(arg)) { }

    T value;
};

请注意使用std::forward实际转发具有正确类型的参数 这至少解决了上述问题,它应该为您提供所需的保证。