程序A 会产生编译错误(按预期),因为使用非整数类型调用isFinite
。
#include <iostream>
class Foo {};
template<typename T>
bool isFinite(const T& t)
{
static_assert(std::is_integral<T>::value, "Called isFinite with a non-integral type");
return false;
}
int main()
{
Foo f;
std::cout << "Foo is finite? " << ((isFinite(f)) ? "yes" : "no") << "\n";
return 0;
}
但是,稍作修改(参见程序B )允许程序编译(Visual Studio 2013)并生成以下输出。
Foo is finite? yes
#include <iostream>
class Foo {};
template<typename T>
bool isFinite(const T& t)
{
static_assert(std::is_integral<T>::value, "Called isFinite with a non-integral type");
return false;
}
int main()
{
Foo f;
std::cout << "Foo is finite? " << ((true || isFinite(f)) ? "yes" : "no") << "\n";
return 0;
}
似乎程序B 在逻辑OR运算上短路,而不是尝试编译表达式的其余部分。 但是,此应用程序无法使用g ++ 4.8.3(g++ -std=c++11 -o main main.cpp
)进行编译。我得到以下输出。
main.cpp: In instantiation of 'bool isFinite(const T&) [with T = Foo]':
main.cpp:15:56: required from here
main.cpp:8:2: error: static assertion failed: Called isFinite with a non-integral type
static_assert(std::is_integral<T>::value, "Called isFinite with a non-integral type");
^
我的直觉让我相信编译失败是正确的行为但很奇怪Visual Studio 2013成功编译。我的直觉是基于以下事实:预计以下代码无法编译。
#include <iostream>
struct Foo
{
void doOperation1() {}
void doOperation2() {}
};
struct Bar
{
void doOperationA() {}
void doOperation2() {}
};
template<typename T>
void performOperation(T& t, bool value)
{
if (value)
{
t.doOperation1();
}
else
{
t.doOperation2();
}
}
int main()
{
Foo f;
performOperation(f, true);
performOperation(f, false);
Bar b;
performOperation(b, false); // Fails to compile (as expected)
return 0;
}
逻辑运算符是否应该遵循编译时的短路评估规则(即,程序B 的预期编译行为是什么?)
答案 0 :(得分:5)
短路不应该编译true || (whatever_ill_formed)
。 isFinite<Foo>
被实例化为表达式的一部分,在实例化期间它应该被编译,并且在编译期间它应该是静态断言。之后,编译器可能永远不会因为短路而评估isFinite<Foo>(f)
,但静态断言不应该在它期间发生。
目前还不清楚为什么Visual Studio 2013编译程序B.标准只允许在模板从未实例化时绕过模板的语法检查。即使这样,代码仍然形成不良,只需要诊断。缺陷的背后可能是Visual C ++中的内部问题,它不允许Microsoft实现constexpr
。
修改我从@zneak请求标准中添加一些语言律师文本
3.2 / 3
一个名称显示为潜在评估表达式的函数 如果它是唯一的查找结果或a的选定成员,则使用odr 一组重载函数(3.4,13.3,13.4),除非它是纯粹的 虚函数及其名称未明确限定。 [注意:这个 涵盖了对命名函数的调用(5.2.2),运算符重载(Clause 13),用户定义的转换(12.3.2),分配功能 placement new(5.3.4),以及非默认初始化(8.5)。一个 选择复制或移动类类型对象的构造函数是 odr-即使实际上已经忽略了调用 (12.8)。 - 后注]
5.13 / 1
||操作员组从左到右。操作数都是 在上下文中转换为bool(第4条)。如果有的话,它返回true 它的操作数是真的,否则是假的。与|,||不同担保 从左到右的评价;而且,第二个操作数不是 评估第一个操作数的计算结果是否为真。
7.1 / 4
在static_assert声明中,constant-expression应为a 常量表达式(5.19),可以在上下文中转换为bool (第4条)。如果转换后的表达式的值为true, 声明无效。否则,该程序是不正确的, 由此产生的诊断信息(1.4)应包括 string-literal,除了不在基本源中的字符 字符集(2.3)不需要出现在诊断中 消息。
14.7.1 / 3
除非已明确表示功能模板专业化 实例化或明确专门化的函数模板 专业化是在专业化时隐式实例化的 在需要函数定义存在的上下文中引用。