- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
C++11
14.8.2 - Template Argument Deduction -
[temp.deduct]
7 The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. The expressions include not only constant expressions such as those that appear in array bounds or as nontype template arguments but also general expressions (ie. non-constant expressions) inside
sizeof
,decltype
, and other contexts that allow non-constant expressions.
C++14
14.8.2 - Template Argument Deduction -
[temp.deduct]
7 The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. The expressions include not only constant expressions such as those that appear in array bounds or as nontype template arguments but also general expressions (ie. non-constant expressions) inside
sizeof
,decltype
, and other contexts that allow non-constant expressions. The substitution proceeds in lexical order and stops when a condition that causes deduction to fail is encountered.
添加的句子明确说明了在 C++14 中处理模板参数时的替换顺序。
替换顺序是最常不被重视的事情。我还没有找到一篇关于为什么这很重要的论文。也许这是因为 C++1y 还没有完全标准化,但我认为引入这样的改变肯定是有原因的。
问题:
为什么以及何时模板参数替换的顺序很重要?
最佳答案
如前所述,C++14 明确表示模板参数替换的顺序是明确定义的;更具体地说,它将保证按“词汇顺序进行并在替换导致演绎失败时停止。
与 C++11 相比,在 C++14 中编写包含一个规则依赖于另一个规则的 SFINAE 代码要容易得多,我们还将摆脱模板替换的未定义顺序可能使我们的整个应用程序受到影响的情况未定义的行为。
Note :需要注意的是,C++14 中描述的行为一直是预期的行为,即使在 C++11 中也是如此,只是它没有以如此明确的方式措辞。
这种变化背后的原理是什么?
此更改背后的原始原因可以在 Daniel Krügler 最初提交的缺陷报告中找到:
14.8.2 - Template Argument Deduction -
[temp.deduct]
8 If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed if written using the substituted arguments.
[ Note: Access checking is done as part of the substitution process. --end note ]
Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure.
[ Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such side effects are not in the "immediate context" and can result in the program being ill-formed. --end note]
换句话说,发生在非直接上下文中的替换仍然会使程序格式错误,这就是模板替换顺序很重要的原因;它可以改变某个模板的整体含义。
更具体地说,这可能是具有 为 的模板与可用于 SFINAE 的模板和 不是 0x2518194123411 的模板之间的区别
愚蠢的例子template<typename SomeType>
struct inner_type { typedef typename SomeType::type type; };template<
class T,
class = typename T::type, // (E)
class U = typename inner_type<T>::type // (F)
> void foo (int); // preferredtemplate<class> void foo (...); // fallback
struct A { };
struct B { using type = A; };
int main () {
foo<A> (0); // (G), should call "fallback "
foo<B> (0); // (H), should call "preferred"
}
在标记为(G)
的行上,我们希望编译器首先检查(E)
,如果成功则评估(F)
,但在本文讨论的标准更改之前,没有这样的保证。foo(int)
中替换的直接上下文包括:(E)
确保传入的T
有::type
(F)
确保inner_type<T>
有::type
如果(F)
被评估,即使(E)
导致无效替换,或者如果(F)
在(E)
之前被评估,我们的简短(愚蠢的)应用程序将被错误诊断,我们的应用程序将不会被使用。虽然我们打算在这种情况下使用foo(...)
。
注意: 注意SomeType::type
不在模板的直接上下文中;inner_type
中的 typedef 失败将导致应用程序格式错误并阻止模板使用 SFINAE。
这对 C++14 中的代码开发有什么影响?
这一变化将极大地减轻语言律师的生活,他们试图实现保证以某种方式(和顺序)进行评估的东西,无论他们使用的是什么符合标准的编译器。
它还将使模板参数替换以更自然的方式对非语言律师进行操作;从左到右进行替换比 erhm-like-any-way-the-compiler-wanna-do-it-like-erhm-....
没有任何负面影响吗?
我唯一能想到的是,由于替换顺序是从左到右,因此不允许编译器使用异步实现同时处理多个替换。
我还没有偶然发现这样的实现,我怀疑它会导致任何重大的性能提升,但至少这个想法(理论上)有点适合事物的“消极”方面。
例如:如果需要,编译器将无法在实例化某个模板时使用同时执行替换的两个线程,而没有任何机制来像在某个点之后发生的替换从未发生过一样。
故事
注释 :本节将提供一个可能取自现实生活的示例,以描述模板参数替换的顺序何时以及为何如此重要。如果有什么不够清楚,甚至可能是错误的,请告诉我(使用评论部分)。
想象一下,我们正在使用枚举器,并且我们想要一种方法来轻松获取指定 枚举 0x25181924213 的底层 值的 。
基本上我们厌倦了总是不得不写(A)
,而我们理想情况下想要更接近(B)
的东西。auto value = static_cast<std::underlying_type<EnumType>::type> (SOME_ENUM_VALUE); // (A)
auto value = underlying_value (SOME_ENUM_VALUE); // (B)
原始实现
说了这么多,我们决定写一个underlying_value
的实现,如下所示。template<class T, class U = typename std::underlying_type<T>::type>
U underlying_value (T enum_value) { return static_cast<U> (enum_value); }
这将减轻我们的痛苦,并且似乎正是我们想要的;我们传入一个枚举器,并取回基础值。
我们告诉自己这个实现很棒,并请我们的一位同事(唐吉诃德)坐下来审查我们的实现,然后再将其投入生产。
代码审查
唐吉诃德是一位经验丰富的 C++ 开发人员,他一手拿着一杯咖啡,另一只手拿着 C++ 标准。他是如何在双手忙碌的情况下设法编写一行代码的,这是一个谜,但这是另一回事。
他审查了我们的代码并得出结论,该实现是不安全的,我们需要保护std::underlying_type
免受未定义行为的影响,因为我们可以传入一个T
不是枚举类型。
20.10.7.6 - Other Transformations -
[meta.trans.other]
template<class T> struct underlying_type;
Condition:
T
shall be an enumeration type (7.2)
Comments: The member typedeftype
shall name the underlying type ofT
.
注意: 标准指定了underlying_type
的条件,但它没有进一步说明如果使用非枚举实例化会发生什么。由于我们不知道在这种情况下会发生什么,因此用法属于 undefined-behavior;它可能是纯 UB,使应用程序格式错误,或在线订购可食用内衣。
披着 Shiny 盔甲的骑士
唐对我们应该如何始终尊重 C++ 标准大喊大叫,我们应该为我们所做的感到非常羞耻......这是 Not Acceptable 。
在他冷静下来并再喝几口咖啡后,他建议我们更改实现以增加保护,防止使用不允许的内容实例化std::underlying_type
。
template<
typename T,
typename = typename std::enable_if<std::is_enum<T>::value>::type, // (C)
typename U = typename std::underlying_type<T>::type // (D)
>
U underlying_value (T value) { return static_cast<U> (value); }
风车
我们感谢 Don 的发现,现在对我们的实现感到满意,但直到我们意识到模板参数替换的顺序在 C++11 中没有明确定义(也没有说明替换何时停止)。
编译为 C++11,我们的实现仍然会导致std::underlying_type
的实例化,其中T
不是枚举类型,原因有两个:
编译器可以在 (D)
之前自由评估(C)
,因为替换顺序没有明确定义,并且;即使编译器在 (C)
之前计算了(D)
,也不能保证它不会计算(D)
。当 C++1 链没有明确说时,必须停止替换链子句。
Don 的实现将不受 C++14 中未定义行为的影响,但这只是因为 C++14 明确声明替换将按词法顺序进行,并且只要替换导致推导失败,它就会停止。
Don 可能不会在这个问题上与风车作斗争,但他肯定错过了 C++11 标准中非常重要的一条龙。
C++11 中的有效实现需要确保无论模板参数替换发生的顺序如何,std::underlying_type
的实例化都不会是无效类型。
#include <type_traits>
namespace impl {
template<bool B, typename T>
struct underlying_type { };
template<typename T>
struct underlying_type<true, T>
: std::underlying_type<T>
{ };
}
template<typename T>
struct underlying_type_if_enum
: impl::underlying_type<std::is_enum<T>::value, T>
{ };
template<typename T, typename U = typename underlying_type_if_enum<T>::type>
U get_underlying_value (T value) {
return static_cast<U> (value);
}
注意:underlying_type
之所以被使用,是因为它是一种使用标准中的东西来对抗标准中的东西的简单方法;重要的一点是用非枚举实例化它是未定义的行为。
之前在这篇文章中链接的缺陷报告使用了一个更复杂的示例,该示例假设对此事有广泛的了解。我希望这个故事对那些没有很好地阅读这个主题的人来说是一个更合适的解释。
关于c++ - 为什么模板参数替换的顺序很重要?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22368022/
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!