如何在c++ 11中使用auto关键字返回任意类型
How to return arbitrary type in C++11 using auto keyword?
我有一个类,看起来像这样:
class Container {
public:
Container(){
Doubles["pi"] = 3.1415;
Doubles["e"] = 2.7182;
Integers["one"] = 1;
Integers["two"] = 2;
}
// Bracket.cpp:23:9: error: 'auto' return without trailing return type
// auto& operator[](const std::string&);
auto& operator[](const std::string& key);
private:
std::map<std::string, double> Doubles;
std::map<std::string, int> Integers;
};
我想重载operator[]
函数,根据传递的密钥从Doubles
或Integers
返回一些东西。但是,我不知道a prioi是否将返回的是double
或int
。我想用这种方式实现operator[]
函数:
// Compiler error
// Bracket.cpp:30:1: error: 'auto' return without trailing return type
// auto& Container::operator[](const std::string& key){
auto& Container::operator[](const std::string& key){
std::cout << "I'm returning the value associated with key: "
<< key << std::endl;
auto D_search = Doubles.find(key);
if (D_search != Doubles.end()){
std::cout << "I found my key in Doubles with value: " <<
D_search->second << std::endl;
return D_search->second;
}
else{
auto I_search = Integers.find(key);
if (I_search != Integers.end()){
std::cout << "I found my key in Integers with value: " <<
I_search->second << std::endl;
return I_search->second;
}
else{
std::cout << "I didn't find a value for the key." << std::endl;
}
}
}
是否有一种方法来创建一个单一的operator[]
函数返回多个类型?
这是由下面的简单代码驱动的:
int main(){
Container Bucket;
double pi(Bucket["pi"]);
std::cout << "The value of pi is: " << pi << std::endl;
return 0;
}
c++ 11版本的auto
作为返回类型只允许您将返回类型声明推迟到函数参数声明之后:
template<typename T1, typename T2>
auto add(T1 x, T2 y) -> decltype(x + y) { return x + y; }
这里不能写decltype(x + y) add(...)
,因为编译器不知道x
和y
是什么。
c++ 14版本的auto
允许编译器对返回类型进行演绎。它告诉编译器根据函数体推断函数的返回类型,但它仍然是一个单一的返回类型,所以它仍然没有做你想做的事情。
不,你不能那样做。一个函数必须有一个在编译时已知的返回类型;当编译器可以从上下文中找出它必须是什么时,auto
只是使您不必使用类型它。
可以使用带标签的联合,或者使用包装联合的类,比如Boost。变量,根据运行时决策保存不同类型的值。这可以使您获得具有可变返回类型的效果。
c++缺乏返回多个不相交类型的函数。
您可以通过使用boost::variant
或类似的标记联合来模拟该语言特性,但这将使调用者的工作变成获得正确的类型。您还可以使用apply函子(boost::variant
使用访问者语法)来处理两个重载(多亏了[](auto){}
lambdas,用c++编写要容易得多)。
如果假设调用者知道类型,则可以根据类型公开两个不同的函数。如果您想要一个相对统一的接口,您可以使用基于类型的专门化的template
函数,但我只是有时建议这样做。如果需要,异常或错误可以详细说明"你输入了错误的类型"(例如,如果你有一个规则,每个名称只映射到一种类型)。
如果您不假设调用者知道类型,那么您必须返回一个对象,该对象允许(在运行时)对该对象的类型进行某种查询。boost::variant
的visitor
是这样做的一种方法,因此它们在事后获得类型安全视图。IUnknown
/dynamic_cast
样式的查询也很流行,但老实说相当乏味。
你也可以做一些技巧。
您可以假设调用者知道标记数据的类型,并返回具有operator int
和operator double
重载的伪引用,并做一些事情(抛出?投吗?断言?返回0
?),如果调用者得到错误的类型。
您可以使用异常来模拟多种返回类型:
int get_foo( std::string bob ) {
if (bob == "alice")
throw 3.14;
else
return 7;
}
int main() {
try {
int x = get_foo("alice");
std::cout << x << "n";
} except( double d ) {
std::cout << d << "n";
}
}
但那太可怕了。
可以想象有一种语言支持这个:
int|double get_foo( std::string bob ) {
if (bob=="alice")
return double{3.14};
else
return int(7);
}
int main() {
int|double x = bob("alice");
std::cout << x << "n";
}
作为汇编级别,将2个返回位置推入bob
(取决于它返回的返回类型)和2个(可能重叠的)写入返回值的位置,然后bob
将写入其中一个并返回到调用代码中的适当位置。调用程序中在此之后的所有代码都必须"分叉"以处理两种可能的返回值类型。
但是这种语言不是c++。c++中的所有表达式必须是单一的已知类型,该类型不依赖于运行时形参,甚至不依赖于作为参数传递的constexpr
形参。
boost::variant
模拟了一个不止一种类型的值,但它实际上是一种允许您访问所包含类型的类型(boost::variant<double, int>
)。
您可以检查Doubles
是否有key
键,如果没有,检查Integers
,如果不包含该键则抛出异常:
auto Container::operator[](const std::string& key)
-> decltype(find_double(key) ? Doubles->second : Integers->second);
bool find_double(const std::string& key) const
{
return Doubles.find(key) != Doubles.end();
}
bool find_integer(const std::string& key) const
{
return Integers.find(key) != Integers.end();
}
然后在operator[]
内输入:
return find_double(key) ? Doubles->second
: find_integer(key) ? Integers->second
: throw std::invalid_argument("Could not find key: " + key);
A Hack
只能创建重载多个转换操作符的类:
struct FooReturnProxy {
FooReturnProxy (int val) : val(val) {}
operator int() const {
return val*2;
}
operator std::string() const {
return "Dizzle izzle dolizzle black boom shackalack tempizzle";
}
// and so on
private:
int val;
};
FooReturnProxy foo() {
return FooReturnProxy(42);
}
int main () {
int x = foo():
std::string y = foo();
}
还可以将这些转换操作符作为模板。
或者,您可以使用boost::any
来允许任何类型。
然而,考虑到在c++社区中没有人真正使用这种方法,这充其量只能被认为是一种hack。真正的解决方案可能需要您在更高的层次上检查您的设计。你真的需要吗?或者你只是想要它?如果只是后者,最好不要使用这里提供的解决方案(当然,除了探索c++)。
c++被设计成静态类型是为了安全。然而,你可以重新设计,从内到外使用多态性来获得你想要的效果。
这个想法是定义一个通用的数字类型,在查找映射中存储键/对象引用。双数列的子类别Int实现所需的操作,如字符串化,数学运算和转换,以允许精确的整数加减或方便的浮点乘法&部门。面向对象设计的优势在于,如果以后您需要添加更多的实现类型的类数,可能是为了无限的精度,而不会影响使用对象方法开发的算法。对于桌面计算器类型的问题,这很可能过于复杂。
- 考虑到其他好处,关键字'auto'真的有助于简化调试C++吗?
- C++/11 auto 关键字是在更有效时推导参数进行按引用传递,还是始终按值传递?
- auto 关键字在 Dev c++ 中不起作用
- 为什么我需要明确编写"auto"关键字?
- 在C++中使用 auto 关键字
- 在 Objective-C++ 中应用于__weak指针时,通过关键字推导类型"auto"规则是什么?
- 在 VS Code 中编码时不能在 C++ 中使用 auto 关键字
- 避免使用 auto 关键字从字面上复制 const 和非 const 的代码?
- 省略C++可变参数 lambda 中的"auto"关键字?
- 我可以在动态知道其类型的模板类对象中使用 auto 关键字吗?我不能在没有初始值设定项的情况下使用 auto 关键字吗?
- 使用 auto 关键字初始化字符数组
- auto*在编译时有用吗?auto关键字足够了吗
- lambda 中的跨平台"auto"关键字用法:integral_constant作为函数参数
- 使用 C++11 auto 关键字声明两个(或多个)变量
- C++11: "auto"关键字会检索到cv限定符吗?我有矛盾的样本
- 如何在C++中使用关键字"auto"声明和使用变量?
- 非常有用的关键字auto的使用
- 关键字"auto" C++和 C# "dynamic"
- 新关键字"auto" ;什么时候应该使用它来声明变量类型?
- 关键字"auto"接近临界点