为了提高对主题的理解,我正在阅读关于模板的C++14标准部分,无意中发现了这个特殊的规则:
§14.1
12在同一范围内,两个不同的声明不应给模板参数默认参数。示例:模板类X;模板类X{/∗.∗/ };//错误-结束示例
在我(相对不知情的)阅读中,“相同范围”的规范意味着能够从定义模板的地方在不同的范围中声明模板。
根据这篇关于多布斯博士的文章
C++确定了五种作用域:函数、函数原型、本地、命名空间和类。
我的理解是:
因此,允许在定义的作用域之外声明的潜在奇怪情况(可能使用更改的默认参数,取决于范围!)似乎完全落在了命名空间范围的肩膀上。我做了一些实验:
[柯尔鲁]
命令:
g++ -std=c++1z -O2 -Wall -pedantic -pthread main.cpp && ./a.out代码:
#include <iostream>
namespace math{
template <int I, int J>
struct Plus{
constexpr static int Value_ = I + J;
};
template <int I, int J = 5>
struct Minus; // template declaration.
}
// global-scope template declaration?
//template <int I, int J>
//struct math::Minus; // error: does not name a type (if no declaration @ line 9)
// error: invalid use of math::Minus w/o argument list
// (if declaration @ line 9)
namespace math{
template <int I, int J>
struct Minus{
static int value();
};
namespace{ // anonymous namespace
//template <int I, int J = 5>
//struct Minus; compiles, but is a declaration of another class,
// which I assume hides the math scope class
// error: incomplete type--I assume this means the above
// doesn't declare math::Minus, but math::<anon>::Minus
//constexpr int twominus5= Minus<2>::value();
} // end anonymous namespace
} // end math namespace
//template <int I, int J>
//class math::Minus; // error: invalid use of math::Minus w/o argument list
template <int I, int J>
int math::Minus<I,J>::value(){return I - J;}
int main()
{
std::cout
<< math::Minus<5,1>::value() << std::endl
<< math::Minus<0>::value() << std::endl;
}输出:
4
-5...and声明规则似乎符合我在阅读这个标准的小片段之前的预期。很明显,我的理解是错误的。这是因为我最初阅读了c++标准的模板默认参数声明子句,还是我忽略了一些在其本机作用域之外声明模板的方法?
当然,这种语言的一个奇怪的角落(如果它确实存在的话)应该谨慎谨慎地使用,特别是因为它会改变其他部分指定的模板的行为,这取决于最适用的范围(它会引起名称冲突问题吗?在没有默认声明的范围内,如果模板定义的范围中有默认参数,那么一个完全限定的名称会如何解析?),但这引起了我的好奇心。
不管怎么说,我都会使用别名,因为这对每个人来说都不那么模糊,但正如我前面所说的:我现在很好奇,这是一个我完全没有刻意使用的奇怪的语言特性,还是一个我想象不到的非特性。
发布于 2015-06-06 11:52:38
我不确定我能完全理解你的想法,但我认为标准只是使用了过于清晰的措辞。这可能是为了澄清不同作用域中的“相同”模板可能具有不同的默认参数。示例:
namespace A
{
template< int = 42 > struct X;
}
namespace B
{
template< int = 123 > struct X;
namespace C
{
template< int = 0 > struct X;
}
}当然,这些模板并不是同一个模板(即使初学者乍一看可能会这么认为),但它们是不同的模板。标准的措辞很可能就是为了强调这一点。
可以有不同默认值的一个示例是带有using的模板别名。
#include <iostream>
#include <type_traits>
namespace A
{
template< int I = 42 >
struct X { static void f() { std::cout << I << std::endl; } };
}
namespace B
{
template< int I = 0 >
using X = A::X< I >;
}
int main()
{
A::X<>::f();
B::X<>::f();
static_assert( std::is_same< B::X<>, A::X<0> >::value, "Oops" );
}实例化
这方面的问题是,它一开始看起来与您的描述相匹配,但有趣的是,虽然B::X<>和A::X<0>是相同的类型,但B::X目前并不是与A::X相同的模板。有关详细信息,请参阅这个答案。
不过,有一个DR (CWG第1286期)可以解决这个问题。不同的默认参数OTOH是DR中提到的一个问题,因此即使DR被解决,它也可能不允许不同的默认值。
发布于 2015-06-06 13:52:18
相同模板的不同作用域中不同的默认参数的一个例子是,当它们在不同的翻译单元中声明时:
// x.hpp
template <class T> class X;
// a.cpp
#include "x.hpp"
template <class T = int> class X;
// b.cpp
#include "x.hpp"
template <class T = float> class X;在这里,我们有两个转换单元创建两个范围,它们都声明相同的实体(类模板X)。在每个全局范围内,我们都有一个新的X声明,其中包含不同的默认模板参数。这是好的,因为他们在不同的范围。
https://stackoverflow.com/questions/30679813
复制相似问题