我有以下关于std::variant用法的示例:
#include <iostream>
#include <string>
#include <variant>
class Cat {
public:
const std::string& getSound() const { return sound; };
private:
std::string sound = "Meow.";
};
class Dog {
public:
const std::string& getSound() const { return sound; };
private:
std::string sound = "Bark.";
};
class House {
public:
void resetAnimal(const auto&& animal) { v = std::move(animal); }
const auto& getAnimal() const {
return std::visit([](const auto& animal) -> decltype(animal)&
{ return animal; }, v);
}
private:
std::variant<Cat, Dog> v;
};
int main() {
House house;
house.resetAnimal(Cat());
std::cout << house.getAnimal().getSound() << std::endl;
house.resetAnimal(Dog());
std::cout << house.getAnimal().getSound() << std::endl;
return 0;
}有趣的是,它用压缩器编译,从g++-8到g++-10。(使用标志-std=c++17和-fpermissive),并在使用g++-11时失败。如果它编译,它就会像预期的那样工作--打印“Meow.‘”。还有“巴克”在不同的线路上。错误信息看起来如下(g++-11):
In file included from <source>:3:
/opt/compiler-explorer/gcc-11.1.0/include/c++/11.1.0/variant: In instantiation of 'constexpr decltype(auto) std::visit(_Visitor&&, _Variants&& ...) [with _Visitor = House::getAnimal() const::<lambda(const auto:23&)>; _Variants = {const std::variant<Cat, Dog>&}]':
<source>:25:26: required from here
/opt/compiler-explorer/gcc-11.1.0/include/c++/11.1.0/variant:1758:29: error: static assertion failed: std::visit requires the visitor to have the same return type for all alternatives of a variant
1758 | static_assert(__visit_rettypes_match,
| ^~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-11.1.0/include/c++/11.1.0/variant:1758:29: note: '__visit_rettypes_match' evaluates to false
<source>: In member function 'const auto& House::getAnimal() const':
<source>:26:48: error: forming reference to void
26 | { return animal; }, v);
| ^
Compiler returned: 1在使用MSVC进行填充时,我得到了含义非常接近的消息。我的问题是:
g++-11编译示例代码?如果答案是“是”,那怎么回答?-fpermissive使g++压缩器在这种情况下工作呢?我知道继承和模板。我只是有兴趣,有没有可能像我在这个例子中做的那样。
发布于 2021-07-27 16:28:37
gcc-8允许这样做是错误的,当您尝试使用它时会产生坏代码。
暂且不谈std::visit()和std::variant<>的官方定义,从纯语言的角度来看,这是直觉上的情况。
为了演示这一点,让我们问自己一个问题:“getAnimal()的返回类型是什么?”毕竟,这必须在编译时确定。
返回auto的函数的返回类型完全由其参数决定。在这种情况下,只有House this,而没有其他任何东西。因此,变体的当前状态不能影响返回的类型。可能是什么?也许是某种推断的受抚养人variant<>?但是这样就不能直接调用getSound()了,所以不可能是它。
让我们不要再想了,我们可以用typeid()自己检查一下
using T = decltype(std::declval<House>().getAnimal());
std::cout << typeid(T).name() << "\n";
// ...
result:
3Cat看来我们只会把猫从这个功能中救出来!我们可以通过稍微修改代码来确认这一点:
class Cat {
public:
const std::string& getSound() const {
std::cout << "I am cat\n";
return sound;
};
private:
std::string sound = "Meow.";
};
class Dog {
public:
const std::string& getSound() const {
std::cout << "I am dog\n";
return sound;
};
private:
std::string sound = "Bark.";
};
//...
result:
I am cat
Meow.
I am cat <--------- !!!!!!
Bark.在您的示例中,它“工作”这一事实是一个小奇迹,因为Cat和Dog恰好具有等效的内存布局。
它仍然是未定义的行为,即使它“有效”。
发布于 2021-07-27 16:28:31
对于科技促进发展:访问,访问者是一个可调用的函数,它可以从变体返回相同类型的R。您的访客返回不同类型。GCC的libstdc++直到GCC11才检查这条规则。这是更新后,诊断添加了:libstdc++:修正访问者返回类型诊断[PR97449]。
https://stackoverflow.com/questions/68548299
复制相似问题