为什么std :: get无法使用变量?

时间:2019-05-30 19:12:51

标签: c++ templates c++14

我在理解函数,特别是模板函数和局部变量在编译期间的行为时遇到了麻烦。

因此,此代码可与std::get配合使用:

enum class UserInfoFields{name, email, address};

using UserInfo = std::tuple<std::string, std::string, std::string>;

int main()
{
    UserInfo s{"Edmund", "edmund@page.me", "Denver street 19"};
    std::cout << std::get<static_cast<size_t>(UserInfoFields::name)>(s) << std::endl;
    return 0;
}

据我了解,这是因为std::get是模板函数,并且需要在编译过程中知道模板参数。这是有道理的,因为static_cast<...在编译期间为我们提供了价值。

如果我将main()代码更改为以下内容,我将无法理解:

 int main()
{
    UserInfo s{"Edmund", "edmund@page.me", "Denver street 19"};
    auto a = static_cast<size_t>(UserInfoFields::name);
    std::cout << std::get<a>(s) << std::endl;
    return 0;
}

这是不允许的。我知道我必须使用constexpr,但是我想知道,为什么第二个代码确实不起作用?

3 个答案:

答案 0 :(得分:5)

你自己写的

  

std::get是模板函数,需要在编译过程中知道模板参数

在编译过程中,局部变量的值不是(通常)。局部变量的值是运行时属性。因此,局部变量不能用作模板参数。

如果要将其用作一个,则必须将其设置为编译时值。这可以通过使其为constexpr(如您在问题中也提到的)来实现。

答案 1 :(得分:3)

模板非类型参数,例如size_t所采用的std::get<>,必须是编译时间常数。

您的auto a不是编译时间常数。在您的特定情况下,您可以证明此时的a值永远不会改变,并且始终为0

但是C ++是一种强类型语言,它依赖于程序员提供的显式类型。在评估std::get<a>的那一点上,C ++唯一允许自己了解a的地方是它是类型为std::size_t的非常量非常量表达式局部变量。

因此,如果std::get<a>有效,则必须:

int main(int argv, char ** argc) {
  UserInfo s{"Edmund", "edmund@page.me", "Denver street 19"};
  std::size_t a = argv; // number of arguments
  std::cout << std::get<a>(s) << std::endl;
}

std::get<std::size_t>nothrow函数,不允许它在运行时失败。如果使用100个参数调用此代码,则上面的代码可能无法正常工作。

第二,虽然您的UserInfo是3个相同的类型,但是std::get<size_t>(tuple<a,b,c>)在类型不相同时起作用。所以

using UserInfo = std::tuple<int, std::string, double>;

然后std::get< argv >( some_user_info )也必须工作。在这种情况下,它返回的类型可以是三种类型中的任何一种-但C ++要求所有表达式都具有 one 类型。

简称为“语言标准如此”。较长的版本是“在一般情况下,您的代码不起作用”。

现在,您可以通过一些更改来解决特定情况下的代码。

using UserInfo = std::array<std::string, 3>;

现在已知UserInfo具有3种统一类型。

std::cout << s[a] << std::endl;

现在您传入索引,并且由于[]的参数不是模板非类型参数,因此它在运行时可能会有所不同。

如果索引超出范围,则允许

[]执行UB。 (不是std::get<a>

现在,C ++可能会发展,新标准可能会变魔术,并以某种方式检测到您的特殊情况,并允许std在运行时失败,等等。但是,每次对std::get的调用都可能导致运行时失败,而以前不是您的应用程序的测试界面刚刚爆炸。

哎呀,它可以自动检测到auto a = blah是在前一行用常量表达式初始化的,并在下一行自动将其用作常量表达式。

但是,知道自己在做什么的程序员用get_proper_a()替换了对3的调用以进行调试时,由于秘密类型信息“泄漏”到了代码中,他们的代码可能会虚假地更改行为。当实际返回get_proper_a()(但编译器无法证明)的3运行时,代码在运行时中断。

答案 2 :(得分:1)

这不是正式的解释,而是(希望)易于理解的原理。

由于C ++是一种强类型语言,因此编译器在编译时必须知道std::get的返回类型。但是std::get的不同版本将返回不同的类型-因为元组中不同的索引可能有不同的类型。

因此,std::get模板参数的值需要来自编译器在编译时已知的位置。 constexpr变量是具有编译器已知值的东西,例如,const变量已使用常量表达式初始化。但是,即使在定义时初始化,简单的非常量整数变量也不是。