构造函数参数的 SFINAE
SFINAE for constructor parameters
我正在尝试创建一个示例 class,它根据其模板参数是否可以用某些参数构造而具有不同的专业化。在我的示例中,使用简单的 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 使用两个 class 进行测试,一个接受 int
作为参数,另一个不接受。
为什么第一种方法不起作用?
我对你问题背后的想法的最好解释是:
对于给定的实例化类型 U
=> A_t
,如果转换
U(int)
未定义然后替换 U(int)
=> 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
=> decltype(A_t(int()))
约束 U
比
光秃秃的 U => A_t
.
这个推理隐含地依赖于 可推导 对于 Teedledee
即 U
= decltype(A_t(int()))
当 U
=> A_t
;
归结为可以推断出 U(int)
= A_t(int)
在唯一的模板参数 decltype(A_t(int()))
.
你相信是这样,然后想知道 gcc 如何选择 Tweedledum
。
正如@WhozCraig 所指出的,clang++ 明确拒绝了您的可推导性前提:-
$ 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>
^
事实证明,gcc 在这里被引入了诊断错误
你的 SFINAE 措辞不吉利,decltype(A_t(int()))
如果将其替换为 decltype(A_t{int()})
,则 gcc 也会 gets with the program:
$ 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’
在那之后,clang++ 是三个编译器中唯一的
完全编译程序 - 并从基本模板实例化 creator<A>
。
A_t
的最终共识 不能 在 decltype(A_t{int()})
中推导
得到 C++14 标准的认可:
Deducing template arguments from a type [temp.deduct.type]
1 Template arguments can be deduced in several different contexts, but in each case
a type that is specified in terms of template parameters (call it P) is compared
with an actual type (call it A), and an attempt is made to find template argument
values (a type for a type parameter, a value for a non-type parameter, or a
template for a template parameter) that will make P, after substitution of the
deduced values (call it the deduced A), compatible with A.
...
4 In most cases, the types, templates, and non-type values that are used to compose
P participate in template argument deduction... In certain contexts, however,
the value does not participate in type deduction, but instead uses the values
of template arguments that were either deduced elsewhere or explicitly specified.
If a template parameter is used only in non-deduced contexts and is not explicitly
specified, template argument deduction fails.
5 The non-deduced contexts are:
...
(5.2) - The expression of a decltype-specifier.
...
第 4 段还解释了为什么您的第二种方法会成功。
我正在尝试创建一个示例 class,它根据其模板参数是否可以用某些参数构造而具有不同的专业化。在我的示例中,使用简单的 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 使用两个 class 进行测试,一个接受 int
作为参数,另一个不接受。
为什么第一种方法不起作用?
我对你问题背后的想法的最好解释是:
对于给定的实例化类型 U
=> A_t
,如果转换
U(int)
未定义然后替换 U(int)
=> 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
=> decltype(A_t(int()))
约束 U
比
光秃秃的 U => A_t
.
这个推理隐含地依赖于 可推导 对于 Teedledee
即 U
= decltype(A_t(int()))
当 U
=> A_t
;
归结为可以推断出 U(int)
= A_t(int)
在唯一的模板参数 decltype(A_t(int()))
.
你相信是这样,然后想知道 gcc 如何选择 Tweedledum
。
正如@WhozCraig 所指出的,clang++ 明确拒绝了您的可推导性前提:-
$ 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>
^
事实证明,gcc 在这里被引入了诊断错误
你的 SFINAE 措辞不吉利,decltype(A_t(int()))
如果将其替换为 decltype(A_t{int()})
,则 gcc 也会 gets with the program:
$ 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’
在那之后,clang++ 是三个编译器中唯一的
完全编译程序 - 并从基本模板实例化 creator<A>
。
A_t
的最终共识 不能 在 decltype(A_t{int()})
中推导
得到 C++14 标准的认可:
Deducing template arguments from a type [temp.deduct.type]
1 Template arguments can be deduced in several different contexts, but in each case a type that is specified in terms of template parameters (call it P) is compared with an actual type (call it A), and an attempt is made to find template argument values (a type for a type parameter, a value for a non-type parameter, or a template for a template parameter) that will make P, after substitution of the deduced values (call it the deduced A), compatible with A.
...
4 In most cases, the types, templates, and non-type values that are used to compose P participate in template argument deduction... In certain contexts, however, the value does not participate in type deduction, but instead uses the values of template arguments that were either deduced elsewhere or explicitly specified. If a template parameter is used only in non-deduced contexts and is not explicitly specified, template argument deduction fails.
5 The non-deduced contexts are:
...
(5.2) - The expression of a decltype-specifier.
...
第 4 段还解释了为什么您的第二种方法会成功。