我正在尝试实现一种紧凑的方法来检测编译时是否有自由函数可用(我使用 std::max
作为示例)。我想到了这个:
#include <stdio.h>
#include <algorithm> // (1)
namespace std { struct max; } // (2)
template<typename A>
concept bool have_std_max = requires(A const& a1, A const& a2) {
{ std::max(a1, a2) }
};
template <typename A>
constexpr A const &my_max(A const &a1, A const &a2) {
if constexpr(have_std_max<A>) {
return std::max(a1, a2);
}
else {
return (a1 > a2) ? a1 : a2;
}
}
int main() {
int x = 5, y = 6;
return my_max(x, y);
}
如果我注释掉 (1),则检测有效并且我的代码使用 constexpr else 分支(参见 in Compiler Explorer)。但是,如果我注释掉 (1) 和 (2),这段代码将无法编译,因为名称 std::max
对编译器来说是未知的。在这种情况下,这个概念不应该简单地返回 false 吗?有没有一种方法可以在不必声明虚拟 max
的情况下实现类似的东西?
最佳答案
C++ 的模板系统绝不是编写错误代码的借口。模板只为所谓的依赖 类型和表达式提供余地。 (这是一个很长的话题,需要更深入地研究 in this answer。)为了我们的目的,一个限定名称的形式是 std::max
。不涉及任何依赖项,因此它必须在出现的地方是正确的。反过来,这意味着查找必须成功。
您尝试添加 max
是在正确的轨道上声明。通过这样做,非依赖限定名总能成功地找到一个声明。同时,整体表达式仍然依赖(由于涉及 a0
和 a1
)。剩下的就是避免污染 std
命名空间,我们不允许这样做:
#include <algorithm>
namespace dirty_tricks {
namespace max_fallback {
// non-deducible on purpose
template<typename Dummy>
void max() = delete;
} // max_fallback
namespace lookup {
// order of directives is not significant
using namespace std;
using namespace max_fallback;
} // lookup
} // dirty_tricks
template<typename Val>
concept bool have_std_max = requires(Val arg) {
// N.B. qualified call to avoid ADL
dirty_tricks::lookup::max(arg, arg);
};
(通过删除 <algorithm>
测试代码时,请确保仍声明了 std
命名空间,否则 using 指令可能会失败。如本 Coliru demo 所示。)
现在dirty_tricks::lookup::max
要么找到两个 std::max
和 dirty_tricks::max_fallback::max
或单独使用后者,但它不会失败。我们还确保我们自己的 max
重载不能通过删除它来产生有效的表达式(否则有效的调用看起来会非常不同)。
关于c++ - 当使用无效表达式时,概念是否应该编译失败?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42646399/