选择哪个类模板特化的首选规则包括将特化重写为函数模板,并通过函数模板[temp.class.order]的排序规则确定哪个函数模板更加专业化.考虑这个例子,然后:
#include <iostream>
template <class T> struct voider { using type = void; };
template <class T> using void_t = typename voider<T>::type;
template <class T, class U> struct A { };
template <class T> int foo(A<T, void_t<T>> ) { return 1; }
template <class T> int foo(A<T*, void> ) { return 2; }
int main() {
std::cout << foo(A<int*, void>{});
}
Run Code Online (Sandbox Code Playgroud)
gcc和clang都打印2在这里.这是有道理的一些前面的例子-推导对非推测的情况下(void对void_t<T>)只是忽略,所以推断<T, void_t<T>>反对<X*, void>成功,但推断<T*, void>针对<Y, void_t<Y>>在两个参数失败.精细. …
#include <iostream>
template<typename T>
struct identity
{
typedef T type;
};
template<typename T> void bar(T) { std::cout << "a" << std::endl; }
template<typename T> void bar(typename identity<T>::type) { std::cout << "b" << std::endl; }
int main ()
{
bar(5); // prints "a" because of template deduction rules
bar<int>(5); // prints "b" because of ...?
return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)
我预计bar<int>(5)至少会产生歧义.这里涉及到关于模板函数重载决策的疯狂规则?
在阅读另一个问题时,我遇到了部分排序问题,我将其缩减为以下测试用例
template<typename T>
struct Const { typedef void type; };
template<typename T>
void f(T, typename Const<T>::type*) { cout << "Const"; } // T1
template<typename T>
void f(T, void*) { cout << "void*"; } // T2
int main() {
// GCC chokes on f(0, 0) (not being able to match against T1)
void *p = 0;
f(0, p);
}
Run Code Online (Sandbox Code Playgroud)
对于两个函数模板,进入重载分辨率的特化的函数类型是void(int, void*).但是,部分排序(根据comeau和GCC)现在说第二个模板更专业.但为什么?
让我通过部分排序,并显示我有问题的地方.可以Q被用于确定根据偏序的独特由上型14.5.5.2.
T1(Q插入)(Q, typename Const<Q>::type*).参数的类型是AT=(Q, void*)T2 …c++ templates partial-ordering function-templates template-argument-deduction
#include <tuple>
template<int I>
struct A {};
template<int I, typename... T>
void f(A<I>, std::tuple<T *...>) {}
template<typename... T>
void f(A<0>, std::tuple<T *...>) {}
int main()
{
f(A<0>{}, std::tuple<char*, int*, float*>{});
}
Run Code Online (Sandbox Code Playgroud)
是不是f更专业的第二次超载?g ++ 4.9.2表示调用不明确,clang 3.6.0接受它.哪个编译器是对的?
有趣的是,如果你改变std::tuple<T *...>对std::tuple<T...>,G ++是与它的罚款,我不明白.
#include <iostream>
#include <cmath>
#include <type_traits>
template <typename T>
void f(T, T) // 1
{
std::cout << "Primary\n";
}
template <typename T>
void f(T, std::enable_if_t<std::is_floating_point_v<T>, T>) // 2
{
std::cout << "Special\n";
}
/*template <typename T>
std::enable_if_t<std::is_floating_point_v<T>> f(T, T) // 3
{
std::cout << "Special\n";
}*/
int main()
{
f(1.1, 1.1); // prints 'Primary'
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在上面的代码中,std::enable_if应用于第二个函数模板重载的函数类型。该函数是通过 [T = double] 推导来调用的,并且它调用重载 1。但是,如果我注释掉重载 2 并将其替换为重载 3,那么编译器会抱怨该调用不明确。我也期望在第一种情况下,为什么编译器更喜欢重载 1 而不是 2?
我阅读了“函数模板重载”部分,但对我来说,重载 2 看起来更专业。
c++ language-lawyer enable-if function-templates-overloading
随着3次超载
template <class T> auto foo() { return 1; }
template <class T> int foo() { return 2; }
template <class T> T foo() { return 3; }
Run Code Online (Sandbox Code Playgroud)
以下是不良形成的?
static_cast<int(*)()>(&foo<int>)();
Run Code Online (Sandbox Code Playgroud)
Clang选择重载#2,而gcc无法编译(Demo)
当删除过载#1时,两者都同意选择过载#2(演示).
当删除重载#2时,gcc选择重载#1并且clang无法编译(Demo)
根据[temp.class.order]§14.5.5.2,t在此示例中选择部分特化:
template< typename >
struct s { typedef void v, w; };
template< typename, typename = void >
struct t {};
template< typename c >
struct t< c, typename c::v > {};
template< typename c >
struct t< s< c >, typename s< c >::w > {};
t< s< int > > q;
Run Code Online (Sandbox Code Playgroud)
等效f于此示例中的重载选择:
template< typename >
struct s { typedef void v, w; };
template< typename, typename = void >
struct t {};
template< typename …Run Code Online (Sandbox Code Playgroud) c++ templates partial-specialization partial-ordering language-lawyer
以下代码打印"func 2".
为什么编译器会在存在显式(未推断)模板参数的情况下将第二个模板视为更好的匹配?为什么没有歧义?
我很感激C++标准的引用.
#include <iostream>
template<class T>
struct identity
{
typedef T type;
};
template<class T>
void func(T)
{
std::cout << "func 1\n";
}
template<class T>
void func(typename identity<T>::type)
{
std::cout << "func 2\n";
}
int main()
{
func<int>(1);
}
Run Code Online (Sandbox Code Playgroud) 我试图重现视频C++ Weekly - Ep 48 - C++ 17的Variadic的结果using,但失败了.问题可以简化为以下代码段.
假设我有这样的通用结构:
template <class... T>
struct Container {
template <class... U>
Container(U... us) {}
};
Run Code Online (Sandbox Code Playgroud)
现在我可以Container用任何参数初始化a ,比如
auto d = Container(1,2,3);
Run Code Online (Sandbox Code Playgroud)
但是,编译器永远不会知道它是什么类型d.要解决这个问题,我们应该提供一个扣除指南,例如
template <class... U>
Container(U...) -> Container<double, int, bool>
Run Code Online (Sandbox Code Playgroud)
根据视频,编译器现在应该知道d有类型Container<double, int, bool>.
但是,代码无法按预期工作.打印时typeid(d).name(),无论我如何更改演绎指南中的返回类型,输出将始终被9ContainerIJEE转换为Container<>,表明此指南根本不指导编译器.
我正在使用gcc-7-snapshot-20170402,视频中的编译器是gcc-7-snapshot-20170130.
谁能告诉我这里有什么问题?
顺便说一句,如果我明确写
Container<bool, int> d = Container(1,2,3);
Container<char, char, char> d = Container(1,2,3);
...
Run Code Online (Sandbox Code Playgroud)
代码将始终编译,并提供像9containerIJbiEE …
这个问题可能很难在标题中的句子中描述,但这是一个最小的示例:
#include <iostream>
#include <type_traits>
template <class T, class U, class Enabler>
struct my_trait : std::false_type
{};
template <class T, class U>
struct my_trait<T, U,
std::enable_if_t<std::is_same<T, U>::value>> : std::true_type
{};
template <class T>
class temped
{};
template <class T>
struct my_trait<temped<T>, temped<T>, void> : std::false_type
{};
template <class T, class U>
using trait_t = my_trait<T, U, void>;
int main()
{
std::cout << std::boolalpha;
std::cout << trait_t<int, float>::value << std::endl; // false
std::cout << trait_t<int, int>::value << std::endl; // true
// …Run Code Online (Sandbox Code Playgroud) 我已经注意到很多关于模板的问题,这些问题是由于部分排序规则造成的,而且它们似乎彼此不一致.我以为我会自己深入了解标准并将其排除在外.忍受我.
gcc和clang似乎都同意用于确定模板排序的算法,但该算法实际上并未出现在标准中.没有特别的顺序:
template <typename T> void foo(T, T); // (1)
template <typename T, typename U> void foo(T, U); // (2)
Run Code Online (Sandbox Code Playgroud)
temp.deduct.type/2清楚地表明Ps必须有一组推导出的值.但是在偏序规则中没有这样的陈述.所描述的算法仅进行成对P/A匹配,因此从(2)到(1)via的合成调用foo(U{}, V{})可以成功推导.gcc和clang都认为(1)更专业.
template <typename T>
struct identity { using type = T; };
template<typename T> void bar(T, T ); // (1)
template<typename T> void bar(T, typename identity<T>::type ); // (2)
Run Code Online (Sandbox Code Playgroud)
这里,如果为(2)Unique2和()合成typename identity<Unique2>::type == Unique2,那么类型推导将在两个方向上成功并且调用bar(0,0)将是模糊的.然而,似乎是两种编译器,而不是简单地把typename identity<Unique2>::type作为Unique2_b,从而由式(2)(1)失败(基于隐含缺少一致性规则)制作模板扣除.
与前面的示例相同,但现在定义:
template <typename T> struct identity; template <> struct identity<int> …Run Code Online (Sandbox Code Playgroud) 我有以下代码:
#include <iostream>
template <class T, typename U = void> class A;
template <class T>
class C
{
public:
typedef T Var_t;
};
template <class T>
class B : public C<T>
{
};
template <class T>
class A<B<T>>
{
public:
A() { std::cout << "Here." << std::endl; }
};
template <class T>
class A<T, typename std::enable_if<
std::is_base_of<C<typename T::Var_t>, T>::value>
::type>
{
public:
A() { std::cout << "There." << std::endl;}
};
int main()
{
A<B<int>> a;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
当编译器尝试使用参数实例化第二个部分专业化时B<int> …