c++模板声明中的作用域和默认实参:澄清标准
Scope and Default Arguments in Template Declarations in C++ : Clarifying Standardese
我在阅读c++ 14标准中关于模板的部分,试图提高我对这个主题的理解,偶然发现了这个特殊的规则:
§14.1
同一作用域内的两个不同的声明不能给模板形参提供默认实参。(例子:
template<class T = int> class X; template<class T = int> class X { /∗... ∗/ }; // error
- end示例]
在我看来,"相同作用域"的规范意味着可以在不同的作用域声明模板,而不是在定义它们的地方。
根据这篇多布斯医生的文章
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
…声明规则似乎符合我在阅读这一小段标准之前所期望的。显然,我的理解在某个地方是错误的。这是我对c++标准的模板默认参数声明子句的初步阅读,还是我错过了在其本机作用域之外声明模板的一些方法?
当然,像这样的语言的一个奇怪的角落(如果它确实存在)应该谨慎使用,特别是因为它会根据最适用的作用域改变其他地方部分指定的模板的行为(它会导致名称冲突问题吗?如果在模板定义的作用域中有默认参数,那么在没有默认声明的作用域中,一个完全限定的名字(甚至)如何解析呢?我将使用别名,因为这对每个人来说都不那么模棱两可,但正如我上面所说的:我现在很好奇这是我完全没有故意使用的一个奇怪的语言特性,还是我只是想象的一个非特性。
我不确定我能完全理解你的想法,但我认为这个标准只是使用了过于清晰的措辞。这可能是为了澄清,在不同作用域中的"相同"模板可能具有不同的默认参数。例子:
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" );
}
<<p> 生活例子/strong> 这个问题是,它看起来像它匹配你的描述,但有趣的是,虽然B::X<>
和A::X<0>
是相同的类型, B::X
是目前不是相同的模板作为A::X
。
在同一模板的不同作用域中声明不同默认参数的一个例子是在不同的翻译单元中声明它们:
// 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声明,带有不同的默认模板参数。
相关文章:
- 非类型引用形参/实参
- c++——关于使用默认实参的困惑
- 调用对象作为默认实参的this
- c++中的默认实参有一些特殊的属性
- 如何传递操作符作为默认函函数实参
- 带默认模板实参的形参包
- 默认模板实参可以对应于特化吗?
- 如何在c++函数模板中指定默认的非模板实参初始化式
- 在c++中真的不可能跳过带有默认实参的模板形参吗?为什么语法不这么认为?
- 为int实参创建一个带有默认情况的转换用例.但是默认情况下无法正确处理字符
- c++默认实参值使用另一个形参的值
- 将vector传递给函数时,默认实参(如果有的话)应该是什么?
- 模板默认实参SFINAE对clang有歧义,但对g++很好
- 继承带有默认实参的构造函数
- 通过引用访问默认实参是否安全
- c++模板声明中的作用域和默认实参:澄清标准
- c++中的函数重载与默认实参
- 指定默认实参的友元声明必须是定义
- 模板形参重新定义默认实参
- 形参列表中间的默认实参