cow*_*cow 30 c++ templates if-statement constexpr c++17
我正在尝试使用 C++17if constexpr
进行条件编译,但它的行为并不符合我的预期。
比如下面的代码,C++还是编译宏定义的代码X2
,
#include <map>
#include <string>
#include <iostream>
#include <type_traits>
#define X1 pp("x")
#define X2 pp("x", "x")
void pp(const std::string str)
{
std::cout << str << std::endl;
}
int main()
{
std::map<std::string, int> map;
if constexpr (std::is_null_pointer_v<decltype(map)>)
X2;
else
X1;
}
Run Code Online (Sandbox Code Playgroud)
并吐出此错误消息:
#include <map>
#include <string>
#include <iostream>
#include <type_traits>
#define X1 pp("x")
#define X2 pp("x", "x")
void pp(const std::string str)
{
std::cout << str << std::endl;
}
int main()
{
std::map<std::string, int> map;
if constexpr (std::is_null_pointer_v<decltype(map)>)
X2;
else
X1;
}
Run Code Online (Sandbox Code Playgroud)
如何跳过X2的编译?
JeJ*_*eJo 36
这在模板之外是不可能的!
在模板之外,完全检查丢弃的语句。
if constexpr
不能替代#if
预处理指令:Run Code Online (Sandbox Code Playgroud)void f() { if constexpr(false) { int i = 0; int *p = i; // Error even though in discarded statement } }
知道如何跳过X2 的编译吗?
template<typename T>
void test()
{
if constexpr (std::is_null_pointer_v<T>)
X2;
else
X1;
}
int main()
{
std::map<std::string, int> map;
test<decltype(map)>(); // now chooses the X1
}
Run Code Online (Sandbox Code Playgroud)
感谢@HolyBlackCat和@MSalters。正如他们所指出的,上述解决方案是格式错误的 NDR(因此,使用MSVC 编译器进行编译没有任何意义,另一方面,GCC 和 clang 至少通过提供一些编译器错误来捕捉到这一点),这已在@HolyBlackCat的,回答!
因此我们可以跳过X2的编译吗?
不幸的是,NO,按您的代码!该预处理器将翻译单元的编译之前执行。因此,不能decltype(map)
向#if
指令提供类型信息(即)。因此,对于您的情况,别无他法。
这篇文章的好教训:
constexpr if
混合。我建议PP
对您的情况进行函数重载(当然还有许多其他方法),通过它您可以获得格式良好的代码:
查看演示。
#include <string>
#include <iostream>
#include <type_traits>
#include <map>
void pp(const std::string& str)
{
std::cout << str << std::endl;
}
template<typename... T> void pp(const T&... args)
{
// do something with args!
}
template<typename T>
constexpr void test()
{
if constexpr (std::is_null_pointer_v<T>)
pp("x", "x"); // call with args
else
pp("x"); // call with string
}
Run Code Online (Sandbox Code Playgroud)
if constexpr
并不是真正的“条件编译”。
在模板之外,它的工作方式与常规一样if
(除了它希望条件为constexpr
)。
其他答案建议将它放在模板中(并使条件取决于模板参数),但这还不够。(它似乎在 MSVC 中有效,但在 GCC 和 Clang 中无效。)那是因为:
[temp.res]/8.1
(强调我的)可以在任何实例化之前检查模板的有效性。... 程序格式错误,无需诊断,如果:
— 不能为模板或模板中的 constexpr if 语句的子语句生成有效的特化,并且模板未实例化,...
因此,如果您不能为if constexpr
分支进行有效的实例化(也就是说,如果对于所有可能的模板参数,分支都是无效的),那么该程序就是格式错误的 NDR(这实际上意味着“无效,但编译器可能不是足够聪明,可以给你一个错误”)。
(正如@MSalters 所指出的,标准说“并且模板没有被实例化”,而不是“如果没有被实例化,模板或 constexpr 的子语句”。我认为这是一个有缺陷的措辞,因为它没有意义否则:似乎没有被任何其他措辞来检查废弃树枝的有效性,所以它会使代码合式只有当封闭的模板被实例化,并形成不良的NDR否则看到的。在评论中讨论)。
似乎没有解决方法,也没有针对您的问题的好的解决方案。
您可以使函数调用本身依赖于模板参数,但这可能是作弊,因为它需要隐藏pp
(或执行#define pp …
)。
template <typename F>
void test(F pp) // Note parameter shadowing the global `pp` for the macros.
{
std::map<std::string, int> map;
if constexpr (std::is_null_pointer_v<decltype(map)>)
X2;
else
X1;
}
int main()
{
test([](auto &&... params)
{
pp(decltype(params)(params)...);
});
}
Run Code Online (Sandbox Code Playgroud)
在模板之外,甚至if constexpr
完全检查an 的错误分支。一般来说,为此,人们需要
#if
预处理器指令,if constexpr
代码放入模板中。在您的情况下,您不能使用,#if
因为您的条件取决于预处理器不可用的类型信息。
此外,您不能使用任何可能的模板参数,constexpr if
因为宏的扩展X2
总是格式错误的。
您可能需要重新考虑为什么要拥有一个其扩展永远无效的宏。