"constexpr if" 与优化后的 "if" - 为什么需要 "constexpr"?
"constexpr if" vs "if" with optimizations - why is "constexpr" needed?
C++1z 将引入 "constexpr if" - 一个 if 将根据条件删除其中一个分支。看起来很合理,也很有用。
但是,没有 constexpr 关键字就不行吗?我认为在编译期间,编译器应该知道在编译期间是否知道条件。如果是,即使是最基本的优化级别也应该删除不必要的分支。
例如(见godbolt:https://godbolt.org/g/IpY5y5):
int test() {
const bool condition = true;
if (condition) {
return 0;
} else {
// optimized out even without "constexpr if"
return 1;
}
}
Godbolt 资源管理器显示,即使带有 -O0 的 gcc-4.4.7 也无法编译 "return 1",因此它实现了 constexpr if 的承诺。显然,当 condition 是 constexpr 函数的结果时,这样的旧编译器将无法这样做,但事实仍然存在:现代编译器知道 condition 是否是 constexpr 并且不需要我明确地告诉它。
所以问题是:
为什么 "constexpr if" 需要 "constexpr"?
这很容易通过一个例子来解释。考虑
struct Cat { void meow() { } };
struct Dog { void bark() { } };
和
template <typename T>
void pet(T x)
{
if(std::is_same<T, Cat>{}){ x.meow(); }
else if(std::is_same<T, Dog>{}){ x.bark(); }
}
正在调用
pet(Cat{});
pet(Dog{});
将触发编译错误 (wandbox example),因为 if
语句的两个分支都必须格式正确。
prog.cc:10:40: error: no member named 'bark' in 'Cat'
else if(std::is_same<T, Dog>{}){ x.bark(); }
~ ^
prog.cc:15:5: note: in instantiation of function template specialization 'pet<Cat>' requested here
pet(Cat{});
^
prog.cc:9:35: error: no member named 'meow' in 'Dog'
if(std::is_same<T, Cat>{}){ x.meow(); }
~ ^
prog.cc:16:5: note: in instantiation of function template specialization 'pet<Dog>' requested here
pet(Dog{});
^
将 pet
更改为使用 if constexpr
template <typename T>
void pet(T x)
{
if constexpr(std::is_same<T, Cat>{}){ x.meow(); }
else if constexpr(std::is_same<T, Dog>{}){ x.bark(); }
}
只要求分支是可解析的——只有符合条件的分支才需要格式正确(wandbox example)。
片段
pet(Cat{});
pet(Dog{});
将按预期编译和工作。
C++1z 将引入 "constexpr if" - 一个 if 将根据条件删除其中一个分支。看起来很合理,也很有用。
但是,没有 constexpr 关键字就不行吗?我认为在编译期间,编译器应该知道在编译期间是否知道条件。如果是,即使是最基本的优化级别也应该删除不必要的分支。
例如(见godbolt:https://godbolt.org/g/IpY5y5):
int test() {
const bool condition = true;
if (condition) {
return 0;
} else {
// optimized out even without "constexpr if"
return 1;
}
}
Godbolt 资源管理器显示,即使带有 -O0 的 gcc-4.4.7 也无法编译 "return 1",因此它实现了 constexpr if 的承诺。显然,当 condition 是 constexpr 函数的结果时,这样的旧编译器将无法这样做,但事实仍然存在:现代编译器知道 condition 是否是 constexpr 并且不需要我明确地告诉它。
所以问题是:
为什么 "constexpr if" 需要 "constexpr"?
这很容易通过一个例子来解释。考虑
struct Cat { void meow() { } };
struct Dog { void bark() { } };
和
template <typename T>
void pet(T x)
{
if(std::is_same<T, Cat>{}){ x.meow(); }
else if(std::is_same<T, Dog>{}){ x.bark(); }
}
正在调用
pet(Cat{});
pet(Dog{});
将触发编译错误 (wandbox example),因为 if
语句的两个分支都必须格式正确。
prog.cc:10:40: error: no member named 'bark' in 'Cat'
else if(std::is_same<T, Dog>{}){ x.bark(); }
~ ^
prog.cc:15:5: note: in instantiation of function template specialization 'pet<Cat>' requested here
pet(Cat{});
^
prog.cc:9:35: error: no member named 'meow' in 'Dog'
if(std::is_same<T, Cat>{}){ x.meow(); }
~ ^
prog.cc:16:5: note: in instantiation of function template specialization 'pet<Dog>' requested here
pet(Dog{});
^
将 pet
更改为使用 if constexpr
template <typename T>
void pet(T x)
{
if constexpr(std::is_same<T, Cat>{}){ x.meow(); }
else if constexpr(std::is_same<T, Dog>{}){ x.bark(); }
}
只要求分支是可解析的——只有符合条件的分支才需要格式正确(wandbox example)。
片段
pet(Cat{});
pet(Dog{});
将按预期编译和工作。