可以编译以下代码而不会出现错误:
template <typename T> struct A {
void f() { this->whatever; } // whatever is not declared before
};
int main() {
A<int> a;
}
Run Code Online (Sandbox Code Playgroud)
我知道这是因为this是一个依赖于类型的表达式,它使名称查找whatever被推迟,直到知道实际的模板参数.由于f()在这种情况下从不使用成员函数,因此不A<T>::f存在实例化,并且whatever永远不会执行名称查找.
我可以理解,this如果类模板具有依赖于类型的基类,那么它是类型相关的:
template <typename T> struct B { T whatever; };
template <typename T> struct A : B<T> {
void f() { this->whatever; }
};
int main() {
A<int> a;
}
Run Code Online (Sandbox Code Playgroud)
在解析模板类的定义时A,不可能知道它的基类是什么,这使得this->whatever潜在合法(B<T>可能有一个成员命名whatever).相反,this->whatever一旦在f …
在c ++草案n4606 [dcl.init] 17.6中有一段关于保证副本省略的段落:
- 如果目标类型是(可能是cv限定的)类类型:
- 如果初始化表达式是prvalue且源类型的cv-nonqualified版本与目标类相同,则初始化表达式用于初始化目标对象.[ 示例:
T x = T(T(T()));调用T默认构造函数进行初始化x.- 结束例子 ]- [...]
还有关于它如何运作的问答报告.
对我自己的理解,我引用的规则保证当初始化表达式是prvalue并且源类型的cv-nonqualified版本与目标类相同时,不应该涉及ctors.所以不需要检查复制的存在或移动ctor,这使得下面的代码在C++ 17中是合法的:
struct A {
A() {}
A(A const &) = delete;
A(A &&) = delete;
};
A f() { return A(); } // it's illegal in C++14, and suppose to be legal in C++17
Run Code Online (Sandbox Code Playgroud)
然而,令我发疯的是我在c ++ draft n4606的list-initialization部分找不到类似的规则.我发现的是([dcl.init.list] 3.6)
[...]
- 否则,如果
T是类类型,则考虑构造函数.枚举适用的构造函数,并通过重载决策(13.3,13.3.1.7)选择最佳构造函数.如果转换任何参数需要缩小转换(见下文),则程序格式错误.[...]
由于列表初始化的优先级高于我引用的第一个规则,因此当初始化程序是初始化程序列表时,我们应该考虑列表初始化部分中的规则.我们可以看到,在列表初始化类类型时会考虑构造函数T.所以,继续前面的例子,将
A ff() { return {A()}; }
Run Code Online (Sandbox Code Playgroud)
在C++ 17中合法吗?有人可以找到标准草案指定保证副本省略在列表初始化中如何工作的地方吗?
C++标准14.8.2 $ 7表示:
替换发生在函数类型和模板参数声明中使用的所有类型和表达式中.表达式不仅包括常量表达式如那些出现在数组边界或无类型模板参数而且一般表达式(即,非常量表达式)的内部
sizeof,decltype和其它上下文允许非常量表达式.替换以词汇顺序进行,并在遇到导致演绎失败的条件时停止.[注意:异常规范中的等效替换仅在实例化异常规范时完成,此时如果替换导致无效的类型或表达式,则程序格式不正确. - 结束说明]
该标准提供了一个示例:
template <class T> struct A { using X = typename T::X; };
template <class T> typename T::X f(typename A<T>::X);
template <class T> void f(...) { }
template <class T> auto g(typename A<T>::X) -> typename T::X;
template <class T> void g(...) { }
void h() {
f<int>(0); // OK, substituting return type causes deduction to fail
g<int>(0); // error, substituting parameter type instantiates A<int>
}
Run Code Online (Sandbox Code Playgroud)
为什么打电话g<int>(0)是错误的?trailing-return-type不会T::X导致替换失败?模板功能 …
考虑以下案例:
#include <iostream>
template <class T> void f(T) { std::cout << "#1\n"; } // #1
template <> void f(const int) { std::cout << "#2\n"; } // #2
int main() {
f<const int>(1); // call #1
f<int>(1); // call #2
return 0;
}
Run Code Online (Sandbox Code Playgroud)
似乎#2 f<int>(const int)代替了f<const int>(const int).这里发生了什么?我的第一个想法是const在函数类型转换中丢弃顶级,因此#2的类型是void(int),这导致了特化f<int>(const int).但我不确定.
为什么C++允许这样的语法?我的意思是因为我们不能部分地专门化函数模板,如果我们想要明确地专门化一个,我们就会知道模板参数值.那么为什么C++不仅仅强制程序员在专门化模板函数时显式提供模板参数值?(即我们必须在#template <> void f<int>(const int) { }或中编写#1的template <> void f<int const>(const int) { }特化)除编码方便外,它是否有特殊用途?
我多次被告知“new-expression 将调用operator new来管理动态存储并同时初始化对象”。我对此毫不怀疑。但我想知道,既然operator new是在标准库 header 中声明的<new>,即使我们不包含头文件,我们如何仍然使用 new-expression 。
是operator newC++ 核心语言的一部分还是编译器<new>隐式包含的?
这是source.cpp
#include <iostream>
struct A {
A(int i) : i(i) { std::cout << this << ": A(int)" << std::endl; }
A(A const &a) : i(a.i) { std::cout << this << ": A(A const &)" << std::endl; }
A(A &&a) : i(a.i) { std::cout << this << ": A(A &&)" << std::endl; }
~A() { std::cout << this << ": ~A()" << std::endl; }
private:
int i;
};
int main() {
std::cout << "#1 :" << std::endl;
A a1 = 1; …Run Code Online (Sandbox Code Playgroud) 我发现自己在理解5.3.5 $ 5中C++标准引用的句子时遇到了麻烦:(重点是我的)
如果被删除的对象在删除时具有不完整的类类型,并且完整的类具有非平凡的析构函数或释放函数,则行为是未定义的.
我知道删除不完整类型问题已在SO中多次讨论,我可以理解为什么删除不完整的类类型是未定义的行为.这个问答很好地解释了它.
我无法理解的是关于完整类类型的部分.是否意味着删除完整类的对象有一个非平凡的析构函数或解除分配函数是未定义的行为?如果是这样,请提供一些代码,说明它可能导致的未定义行为.
c++ ×7
c++17 ×1
clang ×1
copy-elision ×1
gcc ×1
new-operator ×1
optimization ×1
substitution ×1
templates ×1