【发布时间】:2011-07-06 17:46:58
【问题描述】:
给定一个模板函数,它不直接使用模板参数进行输入。 C++ 类型推断如何工作?例如给定
template<typename T>
void f(T::value_type){}
这个函数什么时候(如果有的话)类型推理工作?
除了template<typename T1,...>void f(T1,T2,...),还有其他地方可能发生类型推断吗?
与往常一样,引用额外学分的标准。
【问题讨论】:
给定一个模板函数,它不直接使用模板参数进行输入。 C++ 类型推断如何工作?例如给定
template<typename T>
void f(T::value_type){}
这个函数什么时候(如果有的话)类型推理工作?
除了template<typename T1,...>void f(T1,T2,...),还有其他地方可能发生类型推断吗?
与往常一样,引用额外学分的标准。
【问题讨论】:
它永远不会对嵌套类型起作用,除非 T 也是一个参数。
如果您调用f(1),编译器将没有机会找到所有带有嵌套typedef int value_type; 的T。
您可以推断出属于参数类型的类型,例如
template<class T>
void f(std::vector<T>);
【讨论】:
我想这是你的答案:
14.8.2.4 - 从类型 [temp.deduct.type] 推导出模板参数
[...]
-3- [...]
在大多数情况下,用于组成
P的类型、模板和非类型值参与模板参数推导。也就是说,它们可用于确定模板参数的值,并且如此确定的值必须与其他地方确定的值一致。然而,在某些情况下,该值不参与类型推导,而是使用在其他地方推导或明确指定的模板参数的值。如果模板形参仅在非推导上下文中使用且未显式指定,则模板实参推导失败。-4- 未推断的上下文是:
使用qualified-id指定的类型的nested-name-specifier。
一种 template-id 类型,其中一个或多个 template-arguments 是引用 template-parameter的表达式>.
当以包含非推导上下文的方式指定类型名称时,构成该类型名称的所有类型也都是非推导的。但是,复合类型可以包括推导类型和非推导类型。 [示例:如果将类型指定为
A<T>::B<T2>,则T和T2都是不可推导的。同样,如果将类型指定为A<I+J>::X<T>、I、J和T,则为非推导。如果类型指定为void f(A<T>::B, A<T>),则A<T>::B中的T是非推导的,但A<T>中的T是推导的。 ]
您的T::value_type 是一个类型的qualified-id,因此其nested-name-specifier 中的类型是非推导的,必须明确指定。
编辑:此信息来自ISO/IEC 14882:1998。
【讨论】:
template<typename T> 中只有T 被扣除。我真的不知道为什么最佳答案得到了这么多选票。
C++ 并不总是合二为一 ;-) 5 分钟后的好答案比 1 分钟后的平庸答案获得的票数更少,这真是太可惜了分钟。
标准要求您消除依赖类型的歧义:
template<typename T>
void f(typename T::value_type){}
过去,该领域存在一些不太标准的行为,导致代码在一个 (MSVC) 编译器上编译,但在另一个 (GCC) 编译器上编译不上。这些天来,可能是受到 Boost 等高度通用的标准库的影响,编译器似乎只接受正确的代码。
disambiguate identifiers dependent on template arguments(也称为this)有时需要 typename 关键字。可以这样想:在解析模板定义时,您必须为编译器提供足够的信息来完成第一次语法检查。当时还不知道实际的模板参数,并且(C++ 涉及语法)您必须向编译器提示稍后令牌将代表哪种符号
【讨论】:
除非您以某种方式指定T 参数,否则编译器不会尝试以任何方式扣除T。
即使您明确指定它,我相信它只会在提供原始定义的情况下工作,而不是通过 typedefs。
考虑以下示例(使用 clang++ 编译,显然 g++ 失败):
#include <stdio.h>
template <typename T>
void foo(T) {
printf("foo(T)\n");
}
template <typename T>
void foo(typename T::value) {
printf("foo(T::value)\n");
}
struct X {
class value {};
};
struct Z {
typedef int value;
};
struct XZ {
typedef Z value;
};
typedef X::value Xv;
#define CALL(function,param) printf(#function " (" #param ") = "); function(param());
void explicitCalls() {
printf("Explicit calls:\n");
CALL(foo<int>,int);
CALL(foo<X::value>,X::value);
CALL(foo<Z::value>,Z::value);
CALL(foo<XZ::value>,XZ::value);
CALL(foo<Xv>,Xv);
}
void implicitCalls() {
printf("Implicit calls:\n");
CALL(foo,int);
CALL(foo,X::value);
CALL(foo,Z::value);
CALL(foo,XZ::value);
CALL(foo,Xv);
}
int main() {
explicitCalls();
implicitCalls();
}
输出是:
Explicit calls:
foo<int> (int) = foo(T)
foo<X::value> (X::value) = foo(T::value)
foo<Z::value> (Z::value) = foo(T)
foo<XZ::value> (XZ::value) = foo(T)
foo<Xv> (Xv) = foo(T::value)
Implicit calls:
foo (int) = foo(T)
foo (X::value) = foo(T)
foo (Z::value) = foo(T)
foo (XZ::value) = foo(T)
foo (Xv) = foo(T)
【讨论】: