考虑一下这段C++ 11代码:
#include <iostream>
#include <cstddef>
template<typename T> void f(T, const char*) //#1
{
std::cout << "f(T, const char*)\n";
}
template<std::size_t N> void f(int, const char(&)[N]) //#2
{
std::cout << "f(int, const char (&)[N])\n";
}
int main()
{
f(7, "ab");
}
Run Code Online (Sandbox Code Playgroud)
好吧,那么...选择哪个超载?在使用编译器输出溢出bean之前,让我们试着解释一下.
(所有对部分的引用都是针对C++ 11,ISO/IEC 14882:2011的最终标准文档.)
T
从#1推断出int
,N
从#2推断出来3
,两个专业都是候选者,两者都是可行的,到目前为止都很好.哪一个最好?
首先,考虑将函数参数与函数参数匹配所需的隐式转换.对于第一个参数,在任何一种情况下(身份转换)都不需要转换,int
无处不在,所以这两个函数同样好.对于第二个,参数类型是const char[3]
,并且两个转换是:
[13.3.3.1.1]
; 在根据转换序列进行比较时会忽略此转换类别[13.3.3.2]
,因此这与此目的的身份转换 …考虑一下这段C++ 11代码:
#include <iostream>
struct X
{
X(bool arg) { std::cout << arg << '\n'; }
};
int main()
{
double d = 7.0;
X x{d};
}
Run Code Online (Sandbox Code Playgroud)
在初始化过程中,从double到bool的转换范围正在缩小x
.根据我对标准的理解,这是错误的代码,我们应该看到一些诊断.
Visual C++ 2013发出错误:
error C2398: Element '1': conversion from 'double' to 'bool' requires a narrowing conversion
Run Code Online (Sandbox Code Playgroud)
但是,Clang 3.5.0和GCC 4.9.1都使用以下选项
-Wall -Wextra -std=c++11 -pedantic
Run Code Online (Sandbox Code Playgroud)
编译此代码没有错误,也没有警告.运行程序输出1
(不出意外).
现在,让我们深入到陌生的领域.
改变X(bool arg)
以X(int arg)
和,突然间,我们从锵得到一个错误
error: type 'double' cannot be narrowed to 'int' in initializer list [-Wc++11-narrowing]
Run Code Online (Sandbox Code Playgroud)
海湾合作委员会发出警告
warning: …
Run Code Online (Sandbox Code Playgroud) c++ standards-compliance narrowing c++11 list-initialization
此代码来自Bjarne Stroustrup的"C++编程语言"(C.13.8.3实例化绑定点)
template <class T>
void f(T value)
{
g(value);
}
void g(int v);
void h()
{
extern g(double);
f(2);
}
Run Code Online (Sandbox Code Playgroud)
他提到:
这里,f()的实例化点恰好在h()之前,因此在f()中调用的g()是全局g(int)而不是局部g(double)."实例化点"的定义意味着模板参数永远不能绑定到本地名称或类成员.
void h()
{
struct X {}; // local structure
std::vector<X> v; // error: can't use local structure as template parameter
}
Run Code Online (Sandbox Code Playgroud)
我的问题是:
为什么第一个代码有效?g()
稍后声明,我真的得到了G ++ 4.9.2的错误,那时g
没有声明.
extern g(double) - 这是如何工作的?因为在函数重载的情况下返回值无关紧要,那么我们可以在前向声明中错过它吗?
f()的实例化点就在h()之前 - 为什么?在f(2)
被调用时它会被实例化是不合逻辑的?就在我们称之为的地方,它g(double)
已经在范围内了.
"实例化点"的定义意味着模板参数永远不能绑定到本地名称或类成员 - 在C++ 14中是否已更改?我在使用C++(G ++ 4.9.2)时遇到错误,但是在C++ 14(G ++ 4.9.2)中没有出错.
请考虑以下代码:
#include <iostream>
void f(int) { }
void f(int, short) { }
template<typename... Ts> void g(void (*)(Ts...))
{
std::cout << sizeof...(Ts) << '\n';
}
template<typename T, typename... Ts> void h(void (*)(T, Ts...))
{
std::cout << sizeof...(Ts) << '\n';
}
int main()
{
g(f); // #1
g<int>(f); // #2
h(f); // #3
h<int>(f); // #4
}
Run Code Online (Sandbox Code Playgroud)
目的是分别尝试身体中的每条线main()
.我的期望是所有四个调用都不明确并且会导致编译器错误.
我测试了代码:
-Wall -Wextra -pedantic -std=c++14
(-std=c++1y
对于GCC) - 在所有这些情况下都是相同的行为,除了错误信息措辞的细微差别;使用GCC 4.8.4 g++ --std=c++11 main.cpp
输出以下错误
error: unable to deduce ‘auto’ from ‘max<int>’
auto stdMaxInt = std::max<int>;
Run Code Online (Sandbox Code Playgroud)
这个代码
#include <algorithm>
template<class T>
const T& myMax(const T& a, const T& b)
{
return (a < b) ? b : a;
}
int main()
{
auto myMaxInt = myMax<int>;
myMaxInt(1, 2);
auto stdMaxInt = std::max<int>;
stdMaxInt(1, 2);
}
Run Code Online (Sandbox Code Playgroud)
为什么它适用myMax
但不适用std::max
?我们能让它与之合作std::max
吗?
c++ function-templates auto type-deduction template-instantiation
根据一个简单的,侵入式引用计数的对象系统,我有一个template<typename T> class Handle
,它意味着用子类实例化CountedBase
.Handle<T>
拥有一个指向a的指针T
,并且它的析构函数在该指针上调用DecRef
(定义CountedBase
).
通常,这会在尝试使用前向声明限制标头依赖性时导致问题:
#include "Handle.h"
class Foo; // forward declaration
struct MyStruct {
Handle<Foo> foo; // This is okay, but...
};
void Bar() {
MyStruct ms;
} // ...there's an error here, as the implicit ~MyStruct calls
// Handle<Foo>::~Handle(), which wants Foo to be a complete
// type so it can call Foo::DecRef(). To solve this, I have
// to #include the definition of Foo.
Run Code Online (Sandbox Code Playgroud)
作为解决方案,我重写 …
我有一个在头文件中声明的函数模板.该函数是一个归档程序,它应该支持通过项目实现的其他几种类型(类).我们的想法是拥有一个基本模板声明,然后每个类专门针对它自己的类型.
// Archiver.h
template <class T> void archive(Archiver & archiver, const T & obj);
Run Code Online (Sandbox Code Playgroud)
此方法没有实现.现在我创建一个类(比方说Header
),我希望它可以存档.因此,我打算专门研究这种方法.这就是我现在拥有的:
// Header.h
extern template void archive(Archiver & archiver, const Header & obj);
Run Code Online (Sandbox Code Playgroud)
我声明该函数是extern
因为我在.cpp文件中实现它
// Header.cpp
template <> void archive(Archiver & archiver, const Header & obj)
{
// Code here
}
Run Code Online (Sandbox Code Playgroud)
这给了specialization after instantiation
.我也尝试过其他组合:
undefined reference
从另一个编译单元调用该方法时得到那么实现这个是正确的呢?
编辑:
最初我决定使用模板,因为反向过程,unarchiving.基本上我可以写,unarchive<Header>()
而不是unarchive_header()
似乎更合适.
我相信我还应该提到我正在使用Android Studio和Gradle构建系统编译它,这就是为什么我使用gcc而不是g ++.我还给了gcc以下编译器标志:
-std=gnu++11 -fexceptions -fpermissive -lstdc++
Run Code Online (Sandbox Code Playgroud)
-fpermissive
是一种绝望的行为.