当使用无效表达式时,概念是否应该编译失败?
Should concepts fail to compile when an invalid expression is used?
我正在尝试实现一种紧凑的方法来检测编译时是否有可用函数(我使用 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
对编译器来说是未知的。在这种情况下,这个概念不应该只是 return false 吗?有没有一种方法可以实现类似的东西而不必声明一个虚拟 max
?
C++的模板系统从来不是编写错误代码的借口。模板只为所谓的 dependent 类型和表达式提供余地。 (一个很长的主题,需要更深入地研究 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
重载不能通过删除它来产生有效的表达式(否则有效的调用看起来会非常不同)。
我正在尝试实现一种紧凑的方法来检测编译时是否有可用函数(我使用 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
对编译器来说是未知的。在这种情况下,这个概念不应该只是 return false 吗?有没有一种方法可以实现类似的东西而不必声明一个虚拟 max
?
C++的模板系统从来不是编写错误代码的借口。模板只为所谓的 dependent 类型和表达式提供余地。 (一个很长的主题,需要更深入地研究 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
重载不能通过删除它来产生有效的表达式(否则有效的调用看起来会非常不同)。