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

时间:2018-02-08 07:36:17

标签: c++ undefined-behavior one-definition-rule 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;
}

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

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

1 个答案:

答案 0 :(得分:5)

我没有理由相信有问题的计划格式不正确。简单地在代码中包含某些东西取决于类型的完整性,然后在以后使用其他东西取决于类型已经完成的相同类型的完整性,不违反标准。

如果我们有类似

的问题,就会出现问题
inline Something something;  // external linkage
inline void foo() {
    auto& [one] = something;
}

在多个翻译单元中定义,其中std::tuple_size<Something>已经在定义foo的位置完成了one,而在其他单位中,foo已经完成了。{p>}。这似乎肯定会违反ODR,因为实体template <int unused> void foo() { auto& [one] = something; } // define tuple_element and tuple_size foo<42>(); // instantiate foo EXEC master.dbo.sp_addlinkedserver @server = N'HADOOP_QA', @srvproduct=N'', @provider=N'MSDASQL', @datasrc=N'hadoop_qa' EXEC master.dbo.sp_addlinkedsrvlogin @rmtsrvname=N'HADOOP_QA',@useself=N'True',@locallogin=NULL,@rmtuser=NULL,@rmtpassword=NULL GO 的不同副本中收到不同的类型,但是,我实际上无法在标准中找到这样的地方。要合并为多个定义的标准是:

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

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

         
        
    • 一个非易失性const对象,如果对象

      ,则内部或无链接      
          
      • 在D,
      • 的所有定义中具有相同的文字类型   
      • 用常量表达式(8.20)初始化,
      •   
      • 在D和
      • 的任何定义中都没有使用   
      • 在D的所有定义中具有相同的值,
      •   
           

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

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

  •   
  • 在D的每个定义中,指的是重载运算符,对转换函数的隐式调用,   构造函数,运算符新函数和运算符删除函数,应参考相同的函数,或   到D定义中定义的函数;和
  •   
  • 在D的每个定义中,(隐式或显式)函数调用使用的默认参数被视为if   其标记序列存在于D的定义中;也就是说,默认参数受制于   本段中描述的要求(如果默认参数具有默认的子表达式   参数,此要求递归应用)28;和
  •   
  • 如果D是一个具有隐式声明的构造函数(15.1)的类,就好像构造函数是隐式的   在每个使用过度的翻译单元中定义,以及每个翻译中的隐含定义   unit应为D的子对象调用相同的构造函数。
  •   

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

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

xmlns:

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

  

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