我在理解函数,特别是模板函数和局部变量在编译期间的行为时遇到了麻烦。
因此,此代码可与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
,但是我想知道,为什么第二个代码确实不起作用?
答案 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
变量已使用常量表达式初始化。但是,即使在定义时初始化,简单的非常量整数变量也不是。