在模板,在那里,为什么我必须把typename和template上依赖的名字呢?究竟什么是依赖名称?我有以下代码:
template <typename T, typename Tail> // Tail will be a UnionNode too.
struct UnionNode : public Tail {
// ...
template<typename U> struct inUnion {
// Q: where to add typename/template here?
typedef Tail::inUnion<U> dummy;
};
template< > struct inUnion<T> {
};
};
template <typename T> // For the last node Tn.
struct UnionNode<T, void> {
// ...
template<typename U> struct inUnion {
char fail[ -2 + (sizeof(U)%2) ]; // Cannot be instantiated for any …Run Code Online (Sandbox Code Playgroud) 请考虑以下代码段:
struct Base { };
struct Derived : Base { };
void f(Base &) { std::cout << "f(Base&)\n"; }
template <class T = int>
void g() {
Derived d;
f(T{} ? d : d); // 1
}
void f(Derived &) { std::cout << "f(Derived&)\n"; }
int main() {
g();
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,我认为应该在第一阶段查找对fat 的函数调用// 1,因为它的参数的类型是明确的Derived&,因此可以解析为f(Base&)范围中唯一的一个.
Clang 3.8.0同意我的观点,但是GCC 6.1.0没有,并推迟f到第二阶段的查找,在那里f(Derived&)被选中.
哪个编译器是对的?
为什么C++标准为模板定义了两个阶段查找?非依赖声明和定义的查找是否也可以推迟到实例化阶段?
template <int answer> struct Hitchhiker {
static_assert(sizeof(answer) != sizeof(answer), "Invalid answer");
};
template <> struct Hitchhiker<42> {};
Run Code Online (Sandbox Code Playgroud)
在尝试禁用常规模板实例化的同时,static_assert我发现clang即使模板未实例化,上面的代码也会生成断言错误,而gcc只有在Hitchhiker使用除了以外的参数进行实例化时才生成断言错误42.
摆弄我发现这个断言:
template <int answer> struct Hitchhiker {
static_assert(sizeof(int[answer]) != sizeof(int[answer]), "Invalid answer");
};
template <> struct Hitchhiker<42> {};
Run Code Online (Sandbox Code Playgroud)
两个编译器的行为相同:只有在实例化通用模板时,断言才会启动.
标准说什么,哪个编译器是对的?
g++ 4.9.2
clang++ 3.50
Run Code Online (Sandbox Code Playgroud) 考虑以下代码:
#include <iostream>
namespace N {
class A {};
void f(A a) { std::cout << "N::f\n"; }
}
void f(int i) { std::cout << "::f\n"; }
template <typename T>
class Base {
public:
void f(T x) { std::cout << "Base::f\n"; }
};
template <typename T>
class X : public Base<T> {
public:
void g() {
T t;
f(t);
}
};
int main()
{
X<N::A> x1;
x1.g();
X<int> x2;
x2.g();
}
Run Code Online (Sandbox Code Playgroud)
该代码旨在研究名称查找在C++中的工作方式.
如果我用GNU C++(版本6.1.0)编译该程序,它会打印:
N::f
::f
Run Code Online (Sandbox Code Playgroud)
但如果我用Microsoft Visual Studio 2015编译它,它会打印:
Base::f …Run Code Online (Sandbox Code Playgroud) I was reading about template functions and got confused by this problem:
#include <iostream>
void f(int) {
std::cout << "f(int)\n";
}
template<typename T>
void g(T val) {
std::cout << typeid(val).name() << " ";
f(val);
}
void f(double) {
std::cout << "f(double)\n";
}
template void g<double>(double);
int main() {
f(1.0); // f(double)
f(1); // f(int)
g(1.0); // d f(int), this is surprising
g(1); // i f(int)
}
Run Code Online (Sandbox Code Playgroud)
The results are the same if I don't write template void g<double>(double);.
I …
c++ dependent-name function-templates name-lookup unqualified-name
这是代码:
template<typename T>
class base
{
public:
virtual ~base();
template<typename F>
void foo()
{
std::cout << "base::foo<F>()" << std::endl;
}
};
template<typename T>
class derived : public base<T>
{
public:
void bar()
{
this->foo<int>(); // Compile error
}
};
Run Code Online (Sandbox Code Playgroud)
而且,在运行时:
derived<bool> d;
d.bar();
Run Code Online (Sandbox Code Playgroud)
我收到以下错误:
error: expected primary-expression before ‘int’
error: expected ‘;’ before ‘int’
Run Code Online (Sandbox Code Playgroud)
我知道非依赖名称和两阶段查找.但是,当函数本身是模板函数(foo<>()我的代码中的函数)时,我尝试了所有变通方法只是失败了.
我们知道,下面的代码格式错误,因为该成员x位于依赖基类中.但是,更改x为this->x指示的行将修复错误.
template <typename T>
struct B {
int x;
};
template <typename T>
struct C : B<T> {
void f() {
int y = x; // Error!
}
};
int main() {
C<int> c;
c.f();
}
Run Code Online (Sandbox Code Playgroud)
我想解释一下标准中如何指定这种行为.根据[temp.dep]/3:
在类或类模板的定义中,如果基类依赖于模板参数,则在类模板或成员的定义点或在实例化实例化期间,不会在非限定名称查找期间检查基类作用域.类模板或成员.
这似乎解释了为什么x单独使用失败.x在定义点查找名称,不检查基类范围.但是,如果我们使用this->x怎么办?现在名称x是依赖的,其查找被推迟到实例化.但引用的段落似乎暗示即使在实例化时x也不应该找到,因为查找xin 仍然this->x是不合格的查找.
显然,实现不会以这种方式运行,并且人们普遍认为,一旦模板被实例化,就会搜索基类范围.
struct Bar {
template<typename>
void baz() {
}
};
template<typename>
struct Foo {
Bar bar;
Foo() {
bar.baz<int>();
}
};
int main() {
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这段代码编译得很好(在GCC 4.7中),但如果我将调用前缀bar.baz<int>()加上this->,则baz成为需要消除歧义的从属名称template.
bar.baz<int>(); // OK
this->bar.baz<int>(); // error
this->bar.template baz<int>(); // OK
Run Code Online (Sandbox Code Playgroud)
当然this->bar只能参考Bar bar,其成员baz显然是模板?为什么添加this->使代码对编译器不明确?
ps最初,bar是一个需要消除歧义的基类模板的数据成员this->,但为了这个问题,我简化了示例.
我知道默认情况下编译器不能看到"依赖名称".但是我在回答其他SO问题(这里,在这里,最终在C++ faq)时被告知using声明可能有所帮助.
所以我试过了.
模板基类:
// regardless of the fact that members are exposed...
template<typename T>
struct TBase {
typedef T MemberType;
MemberType baseMember;
MemberType baseFunction() { return MemberType(); }
};
Run Code Online (Sandbox Code Playgroud)
和派生类,使用基数的成员:
template<typename T>
struct TDerived : public TBase<T> {
// http://www.parashift.com/c++-faq-lite/nondependent-name-lookup-members.html
// tells us to use a `using` declaration.
using typename TBase<T>::MemberType;
using TBase<T>::baseFunction;
using TBase<T>::baseMember;
void useBaseFunction() {
// this goes allright.
baseFunction();
++baseMember;
// but here, the compiler doesn't want …Run Code Online (Sandbox Code Playgroud) c++ ×10
dependent-name ×10
templates ×8
name-lookup ×5
base-class ×1
c++-faq ×1
c++11 ×1
c++14 ×1
compiler-bug ×1
typedef ×1
typename ×1