首页 文章

static_assert依赖于非类型模板参数(gcc和clang上的不同行为)

提问于
浏览
24
template <int answer> struct Hitchhiker {
  static_assert(sizeof(answer) != sizeof(answer), "Invalid answer");
};

template <> struct Hitchhiker<42> {};

尝试使用 static_assert 禁用常规模板实例化时,我发现即使模板未实例化, clang 中的上述代码也会生成断言错误,而 gcc 仅在使用 42 以外的参数实例化 Hitchhiker 时生成断言错误 .

摆弄我发现这个断言:

template <int answer> struct Hitchhiker {
  static_assert(sizeof(int[answer]) != sizeof(int[answer]), "Invalid answer");
};

template <> struct Hitchhiker<42> {};

两个编译器的行为相同:只有在实例化通用模板时,断言才会启动 .

标准说什么,哪个编译器是对的?

g++ 4.9.2
clang++ 3.50

2 回答

  • 14

    由@TartainLlama发现的行情

    如果由于不依赖于模板参数的构造而在其定义之后立即对模板进行假设实例化,则该程序是不正确的;无需诊断 .

    N4296 [temp.res] / 8

    这在定义主模板后立即应用(其中包含 static_assert 的模板) . 因此,后来的专业化(对于 42 )不能被考虑,因为它还不存在 .

    接下来的问题是 static_assert( sizeof(answer) != sizeof(answer), 是否取决于 answer . 从语义上讲,它没有,从语法上来说,它是标准的:

    在模板内部,一些构造具有可能因实例化而异的语义 . 这种结构取决于模板参数 .

    N4296 [temp.dep] / 1

    构造 sizeof(answer) != sizeof(answer) 与一个实例不同 . 所以这样的构造不依赖于模板参数 . 这意味着整个 static_assert 不依赖于模板参数 .

    因此,您的程序形成不良,无需诊断 . 发出任意诊断(例如 static_assert 失败)是有效的编译器行为 . 缺少问题是有效的编译器行为 . 从标准形成的,没有诊断要求的程序编译的程序的行为不是由标准定义的:它是未定义的行为 . 允许鼻腔恶魔 .

    花哨的尝试(如 sizeof(int[answer])!=sizeof(int[answer]) 可能会让当前的神编译器满意,但不会让您的程序更加完善 .

    你可以提出一个案例,编译器不太可能 grab 你,但不管编译器是否有能力 grab 你,它仍然存在形式错误 . 作为一般规则,C希望让自己(及其编译器)自由地“在实例化之前”找到无效的模板代码;这意味着模板代码必须生成可能合法的代码 .

    您可能需要附带消息的 =delete 之类的内容 .

  • 14

    两个编译器都是正确的 . 来自[temp.res] / 8:

    如果无法为模板生成有效的专业化,并且未实例化该模板,则模板格式错误,无需诊断 .

    不存在可从主模板 Hitchhiker 生成的有效专业化,因此它格式错误,无需诊断 . clang无论如何选择发布诊断 .

    如果您只想允许 42 ,那么只需不要定义通用模板:

    template <int > struct Hitchhiker;
    template <> struct Hitchhiker<42> {};
    

相关问题