SFINAE用于构造函数参数

时间:2016-12-07 21:28:48

标签: c++ class constructor sfinae

我尝试根据模板参数是否可以使用某些参数构建来创建具有不同特化的示例类。在我的示例中,使用简单的int。我试过了:

template<class A_t>
struct creator
{
    A_t* ptr = nullptr;
};

template<class A_t>
struct creator<decltype(A_t(int()))>
{
    A_t* ptr = new A_t(5);
};

struct A
{
    int i;

    A(int i) : i(i) {}
};

int main() {
    std::cout << creator<A>().ptr << std::endl;

    return 0;
}

我的意图是它打印一个自动构造的对象的内存地址。但是,它正在打印0.因此,它采用了非专业模板。

A_t可以使用该语法进行推导,因为A_t是明确给出的。此外,decltype(A_t(int())的类型为A_t(而不是A_t&&),这是一个简单的测试:

std::cout << std::is_same<decltype(A(int()), A>::value << std::endl;

打印1。

但是,该实施有效:

#include <iostream>

template<class A_t, class = A_t>
struct creator
{
    A_t* ptr = nullptr;
};

template<class A_t>
struct creator<A_t, decltype(A_t(int()))>
{
    A_t* ptr = new A_t(5);
};

struct A
{
    int i;

    A(int i) : i(i) {}
};

int main() {
    std::cout << creator<A>().ptr << std::endl;

    return 0;
}

Coliru测试两个类,一个接受int作为参数,另一个不接受。

为什么第一种方法不起作用?

1 个答案:

答案 0 :(得分:1)

我对你的问题背后思考的最好的亮点是:

对于给定的实例化类型U =&gt; A_t,如果转换 U(int)未定义,则替换U(int) =&gt; A_t(int) 将在上下文decltype(A_t(int()))和专业化中失败:

template<class A_t>
struct creator<decltype(A_t(int()))>
{
    A_t* ptr = new A_t(5);
};

将被删除,只留下基本模板实例化。但如果 转换U(int)定义为替换将成功。

然后,因为:

std::is_same<U,decltype(U(int()))>::value == true

两位候选人:

// Tweedledum, with of U => A_t
struct creator<U>
{
    U* ptr = nullptr;
};

// Tweedledee, with U => decltype(A_t(int()))
struct creator<U>
{
    U* ptr = new U(5);
};

正在运行中。

然后,将选择最专业的候选人,即Tweedledee, 因为满足U =&gt; decltype(A_t(int()))比{}更精确地约束UU => A_t

Teedledee =&gt; U = decltype(A_t(int())) U A_t时,这种推理隐含地依赖于{em> deducible deducible U(int); 归结为可以在单一模板参数A_t(int)中推导decltype(A_t(int())) = Tweedledum

你相信这是如此,然后想知道gcc如何挑选$ clang++-3.8 -Wall -Wextra -pedantic -std=c++14 main.cpp main.cpp:10:8: warning: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used struct creator<decltype(A_t(int()))> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ main.cpp:9:16: note: non-deducible template parameter 'A_t' template<class A_t> ^

正如@WhozCraig指出的那样,clang ++明确拒绝你的可引导性前提: -

decltype(A_t(int()))

So does MSVC++

事实证明,gcc已被诱导为诊断失误 你的SFINAE语言的不幸表达,decltype(A_t{int()})

如果您将其替换为$ g++-6 -Wall -Wextra -pedantic -std=c++14 main.cpp main.cpp:10:8: error: template parameters not deducible in partial specialization: struct creator<decltype(A_t{int()})> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ main.cpp:10:8: note: ‘A_t’ ,则gcc也会gets with the program

creator<A>

之后,clang ++是三者中唯一的编译器 完全编译程序 - 并从基本模板中实例化A_t

decltype(A_t{int()}) public class Cards implements Comparable<Cards> { // Ace lowest: public int compareTo(Card other) { return Integer.compare(cardnum, other.cardnum); } } 得到了C ++ 14标准的认可:

  

从类型[temp.deduct.type]

中推导出模板参数      

1模板参数可以在几个不同的上下文中推导出来,但在每种情况下都是如此   根据模板参数(称为P)指定的类型进行比较   使用实际类型(称为A),并尝试查找模板参数   values(类型参数的类型,非类型参数的值或a   模板参数的模板)在替换之后将产生P.   推导出的值(称之为推导出的A),与A兼容。

     

...

     

4在大多数情况下,用于撰写的类型,模板和非类型值   P参与模板参数推导......但在某些情况下,   该值不参与类型推导,而是使用值   模板参数可以在其他地方推断或明确指定。   如果模板参数仅在非推导的上下文中使用,并且未明确表示   指定的模板参数推断失败。

     

5未推断的上下文是:

     

...

     

(5.2) - decltype-specifier的表达式。

     

...

第4段也解释了为什么你的第二种方法成功。