如果 constexpr not-taken 分支不需要有效,确定吗? [stmt.if]/2[temp.res.general]/6.1

问题描述

我正在测试以下代码

#define TO_STRING(X) #X

#define TEST(X,Y) namespace Y { constexpr std::string_view name{#X},raw_value{TO_STRING(X)};\
constexpr bool defined {name!=raw_value}; constexpr bool has_value{raw_value!=""}; }

#define A
#define B 1

TEST(A,a);
TEST(B,b);
TEST(C,c);

int main()
{
    if constexpr (a::defined && a::has_value) {
        std::cout << a::name << " = " << a::raw_value << '\n';
    } else {
        std::cout << a::name << " not defined or have no value\n";
    }
    if constexpr (b::defined && b::has_value) {
        std::cout << b::name << " = " << b::raw_value << '\n';
    } else {
        std::cout << b::name << " not defined or have no value\n";
    }
    if constexpr (c::defined && c::has_value) {
        std::cout << c::name << " = " << c::raw_value << '\n';
    } else {
        std::cout << c::name << " not defined or have no value\n";
    }
    return 0;
}

产生以下输出

A not defined or have no value
B = 1
C not defined or have no value

如果我修改 TEST 添加一个函数

#define TEST(X,raw_value{TO_STRING(X)};\
constexpr bool defined {name!=raw_value}; constexpr bool has_value{raw_value!=""};\
constexpr auto value() { if constexpr (defined && has_value) return X; else return; } }

我期望编译器在扩展 return X 宏时忽略 TEST(C,c) 语句,而是报告错误

error: use of undeclared identifier 'C'
TEST(C,c);
     ^

引用未声明的标识符绝对是格式错误的,但我希望编译器忽略它,因为我认为 if constexpr 未采用的分支不需要格式正确。有没有办法实现这种行为?

解决方法

[stmt.if]/2

如果 ... 条件的值为假,则第一个子语句[又名第一个分支]是一个废弃语句,否则第二个子语句(如果存在),是一个废弃的语句。 在封闭模板化实体的实例化过程中 ...,如果条件在实例化后不依赖于值,丢弃的子语句(如果有)不会被实例化

(我的粗体)

这解释了模板内 if constexpr 的行为。由于没有其他说明,它在模板之外的行为与普通 if 的行为相匹配。

现在,如果您正在考虑通过添加一个未使用的模板参数来使您的函数成为模板,这将不会那么容易:

[temp.res.general]/6.1

程序格式错误,无需诊断,如果:

— 不能为模板或模板中 constexpr if 语句的子语句生成有效的特化,并且模板未实例化

(我的粗体)