您是否可以在类类型上使用结构化绑定来违反ODR

Cur*_*ous 5 c++ one-definition-rule undefined-behavior c++17 structured-bindings

结构化绑定功能表示,如果tuple_size模板是完整类型,它会像分解一样使用元组.如果std::tuple_size在程序中的某一点是给定类型的完整类型并且在另一点未完成,会发生什么?

#include <iostream>
#include <tuple>

using std::cout;
using std::endl;

class Something {
public:
    template <std::size_t Index>
    auto get() {
        cout << "Using member get" << endl;
        return std::get<Index>(this->a);
    }

    std::tuple<int> a{1};
};

namespace {
    auto something = Something{};
}

void foo() {
    auto& [one] = something;
    std::get<0>(one)++;
    cout << std::get<0>(one) << endl;
}

namespace std {
template <>
class tuple_size<Something> : public std::integral_constant<std::size_t, 1> {};
template <>
class tuple_element<0, Something> {
public:
    using type = int;
};
}

int main() {
    foo();
    auto& [one] = something;
    cout << one << endl;
}
Run Code Online (Sandbox Code Playgroud)

(转载于此处https://wandbox.org/permlink/4xJUEpTAyUxrizyU)

在上面的程序中,类型在程序中的Something某一点通过公共数据成员进行分解,然后像另一个程序中的分解一样回退到元组.我们std::tuple_size是否在幕后隐含的" 完整"检查中违反了ODR ?

Bri*_*ian 5

我认为没有理由相信有关方案是不正确的.简单地在代码中包含某些东西取决于类型的完整性,然后在其后继续使用其他东西取决于类型已经完成的相同类型的完整性,不违反标准.

如果我们有类似的东西就会出现问题

inline Something something;  // external linkage
inline void foo() {
    auto& [one] = something;
}
Run Code Online (Sandbox Code Playgroud)

在多个翻译单元中定义,其中一些翻译单元在定义std::tuple_size<Something>的位置已经完成foo,而在另一些翻译单元中则不是.这似乎肯定会违反ODR,因为实体one在不同的副本中收到不同的类型foo,但是,实际上我无法在标准中找到这样的地方.要合并为多个定义的标准是:

  • D的每个定义应由相同的令牌序列组成; 和

  • 在D的每个定义中,根据6.4查找的相应名称,应指在D的定义中定义的实体,或者在重载决议(16.3)之后和部分模板专门化匹配之后应引用同一实体(17.8) .3),但名称可以参考

    • 一个非易失性const对象,如果是对象,则内部或没有链接

      • 在D的所有定义中具有相同的文字类型,
      • 用常量表达式(8.20)初始化,
      • 在D的任何定义中都没有使用过
      • 在D的所有定义中具有相同的值,

      要么

    • 具有内部连接或无链接的引用,使用常量表达式初始化,使得引用引用D的所有定义中的相同实体;

  • 在D的每个定义中,相应的实体应具有相同的语言链接; 和

  • 在D的每个定义中,所引用的重载运算符,对转换函数,构造函数,运算符新函数和运算符删除函数的隐式调用,应引用相同的函数,或者引用D定义中定义的函数; 和
  • 在D的每个定义中,(隐式或显式)函数调用使用的默认参数被视为其标记序列存在于D的定义中; 也就是说,默认参数受本段中描述的要求的约束(并且,如果默认参数具有带默认参数的子表达式,则此要求将递归应用)28; 和
  • 如果D是一个带有隐式声明的构造函数的类(15.1),就好像构造函数是在每个使用odr的翻译单元中隐式定义的,并且每个翻译单元中的隐式定义应该调用相同的构造函数D.的子对象

如果这里有一条规则使我的代码格式不正确,我不知道它是哪一个.也许标准需要修改,因为它不能被允许这样做.

使程序格式错误的NDR的另一种方法是使用模板:

template <int unused>
void foo() {
    auto& [one] = something;
}
// define tuple_element and tuple_size
foo<42>(); // instantiate foo
Run Code Online (Sandbox Code Playgroud)

据此,这会与[temp.res] /8.4发生冲突

该程序格式错误,无需诊断,如果...... [[模板的假设实例化]紧接其定义后的[不依赖于模板参数的构造]的解释]与模板的任何实际实例化中的相应构造