【发布时间】:2016-10-22 05:25:56
【问题描述】:
以下 c++ 代码的目的是将三元运算符 (?:) 包装在一个单独的函数中,这将有助于稍后构建语法树。
在看真正的 c++ sn-p 之前,让我们快速看一下它在伪代码中的作用:
bool recursive(bool v) {
return v ? v : recursive(v);
}
int main() {
bool r = recursive(true)
}
不幸的是,当三元运算符 (?:) 被包装在模板函数中时,Clang 在终止递归时遇到问题:
/****************** DECLARATIONS ******************/
template<typename T>
constexpr T
recursive(T t);
struct IfCase {
template<typename T>
constexpr T
operator()(T t) const;
};
struct ElseCase {
template<typename T>
constexpr T
operator()(T t) const;
};
#if defined(WORKS)
static constexpr bool
if_then_else_return(bool b, IfCase const& ic, ElseCase const& ec, bool x);
#else
template<typename T, typename IfFunctor, typename ElseFunctor>
static constexpr T
if_then_else_return(T b, IfFunctor const& ic, ElseFunctor const& ec, T x);
#endif
/****************** DEFINITIONS ******************/
template<typename T>
constexpr T
IfCase::operator()(T t) const {
return t;
}
template<typename T>
constexpr T
recursive(T t) {
return if_then_else_return(t, IfCase{}, ElseCase{}, t);
}
template<typename T>
constexpr T
ElseCase::operator()(T t) const {
return recursive(t);
}
#if defined(WORKS)
constexpr bool
if_then_else_return(bool b, IfCase const& ic, ElseCase const& ec, bool x) {
return b ? ic(x) : ec(x);
}
#else
template<typename T, typename IfFunctor, typename ElseFunctor>
constexpr T
if_then_else_return(T b, IfFunctor const& ic, ElseFunctor const& ec, T x) {
return b ? ic(x) : ec(x);
}
#endif
/****************** CALL ******************/
int main() {
constexpr auto r = recursive(true);
}
构建结果:
g++ 与 reg.功能(-DWORKS):好的
g++ 与 tmpl。功能:OK
clang++ 与 reg.功能(-DWORKS):好的(Find code & results also at Coliru)
clang++ 与 tmpl。功能:失败(Find code & results also at Coliru)
GCC (4.9.2) 编译两个变体都没有错误,但 Clang (3.5 到 3.8) 失败并显示以下错误消息:
main.cpp:56:14: fatal error: recursive template instantiation exceeded maximum depth of 256
return b ? ic(x) : ec(x);
^
/*** the next error messages for lines 64, 38 and 56 are repeated several times ***/
main.cpp:56:22: note: in instantiation of function template specialization 'ElseCase::operator()<bool>' requested here
return b ? ic(x) : ec(x);
^
main.cpp:38:9: note: in instantiation of function template specialization 'if_then_else_return<bool, IfCase, ElseCase>' requested here
return if_then_else_return(t, IfCase{}, ElseCase{}, t);
^
main.cpp:64:21: note: in instantiation of function template specialization 'recursive<bool>' requested here
constexpr auto r = recursive(true);
^
1 error generated.
但是为什么呢?如何重写这段代码,让 Clang 不再抱怨?
非常感谢您。
编辑 1:
我缩短了编译器消息,希望能提高其可读性。如需完整的回溯,请查看上面提供的 Coliru 链接。
删除
constexpr说明符将解决此 Clang 错误。但这也会减少功能,因此不是一种选择。
【问题讨论】:
-
请在 Coliru here (reg. function) 和 here (tmpl. function) 找到 GCC 的代码和构建结果。
-
请使用minimal reproducible example 示例,走开。在这个问题的情况下,如果您将问题从产生错误的 one 函数中减少为 one 错误,那么帮助您会更容易。
-
感谢您的提示!如果我删除
constexpr,问题就消失了,但这也会减少功能。如果我删除#ifdef等。恕我直言,很难理解我对 reg 的意思。函数和tmpl。功能,但如果有帮助,我可以删除它们。如果没有声明和定义之间的分离,我会遇到 GCC 编译错误。你到底想到了什么? -
你不需要显示所有的错误信息。如果其中一个声明/定义导致错误,只需将其用作示例,而不是全部使用。当然,不要更改代码的功能/目的或使用糟糕的编程实践;这不是我的建议。尝试使用最少量的代码进行编译,从而以最能代表您的问题的方式导致错误。
-
可能是您使用的代码是最少需要的,但有些错误是完全重复的。
标签: c++ templates gcc clang ternary-operator