尾随返回类型的语法样式是否应该成为新的C ++ 11程序的默认样式?[关闭]


92

C ++ 11支持新的函数语法:

auto func_name(int x, int y) -> int;

当前,此函数将声明为:

int func_name(int x, int y);

新样式似乎尚未被广泛采用(例如在gcc stl中)

但是,这种新样式是否应该在新的C ++ 11程序中到处都是首选,还是仅在需要时使用?

就个人而言,我尽可能地选择旧样式,但是混合样式的代码库看起来很难看。


29
它主要用于decltype争论。
Cat Plus Plus

CatPlusPlus所说的话:在您的示例中使用它没有多大意义
stijn 2012年

@Cat Plus Plus这意味着您可以保留C ++ 03中的内容,除非需要派生返回类型?
mirk 2012年

1
很难在每个功能前面都指定“ auto”。就像C ++对python的“ def”的明智回答一样吗?
Erik Aronesty

Answers:


109

在某些情况下,您必须使用尾随返回类型。最值得注意的是,如果指定了lambda返回类型,则必须通过尾随返回类型来指定。同样,如果您的返回类型使用了一个decltype要求参数名称在范围内的,则必须使用尾随返回类型(但是,通常可以使用它declval<T>来解决后一个问题)。

尾随返回类型确实具有其他一些次要优势。例如,考虑使用传统函数语法的非内联成员函数定义:

struct my_awesome_type
{
    typedef std::vector<int> integer_sequence;

    integer_sequence get_integers() const;
}; 

my_awesome_type::integer_sequence my_awesome_type::get_integers() const
{
    // ...
}

在类名出现在之前::get_integers,成员typedef不在范围内,因此我们必须重复两次类限定。如果我们使用尾随返回类型,则无需重复该类型的名称:

auto my_awesome_type::get_integers() const -> integer_sequence
{
    // ...
}

在此示例中,这没什么大不了的,但是如果您有很长的类名或未内联定义的类模板的成员函数,那么在可读性上会产生很大的不同。

在他的“油漆未干”在C ++现在2012会话,阿利斯代尔梅瑞狄斯指出,如果你使用一贯尾随返回类型,所有的函数名排队整齐:

auto foo() -> int;
auto bar() -> really_long_typedef_name;

我到处使用后返回类型在CxxReflect,所以如果你正在寻找如何利用他们的代码看起来一致的例子,你可以看看那里(例如,type)。


1
看起来似乎尚未达成共识,但是以新样式查看CxxReflect很有趣。
mirk

嗨,詹姆斯。根据C ++ 14标准,可以使此答案更准确。
德鲁·多曼

@DrewDormann您将添加/更改什么?
underscore_d

对齐实际上是一个很大的优点,至此,我希望可以有一个新的'func'关键字替换这里毫无意义的'auto'。
约翰·布勒(JohanBoulé)

67

除了其他人所说的,尾随返回类型还允许使用this,否则不允许

struct A {
  std::vector<int> a;

  // OK, works as expected
  auto begin() const -> decltype(a.begin()) { return a.begin(); }

  // FAIL, does not work: "decltype(a.end())" will be "iterator", but 
  // the return statement returns "const_iterator"
  decltype(a.end()) end() const { return a.end(); }
};

在第二个声明中,我们使用了传统风格。但是,由于this不允许在该位置进行编译,因此编译器不会隐式使用它。因此,a.end()使用静态声明的类型a来确定将要调用的end重载类型vector<int>,最终是非const版本。


2
尽管这是一个很好的概念说明(使用返回类型中的成员),但是很有趣,因为在C ++ 14中,在没有转换的内联定义中指定类型是完全多余的;我们现在可以使用完全返回类型推导。:P
underscore_d

27

另一个优点是,当函数返回指向函数的指针时,尾随返回类型的语法可以更易读。例如比较

void (*get_func_on(int i))(int);

auto get_func_on(int i) -> void (*)(int);

但是,可以争论的是,只需为函数指针引入类型别名即可实现更好的可读性:

using FuncPtr = void (*)(int);
FuncPtr get_func_on(int i);

10

看到这篇不错的文章:http: //www.cprogramming.com/c++11/c++11-auto-decltype-return-value-after-function.html很好的示例,何时在游戏中使用不带decltype的这种语法:

class Person
{
public:
    enum PersonType { ADULT, CHILD, SENIOR };
    void setPersonType (PersonType person_type);
    PersonType getPersonType ();
private:
    PersonType _person_type;
};

auto Person::getPersonType () -> PersonType
{
    return _person_type;
}

Alex Allain的文章还窃取了出色的解释:“因为返回值位于函数的末尾,而不是在函数的末尾,因此不需要添加类范围。”

与这种可能的情况进行比较:当一个偶然的事件忘记了类范围,而对于更大的灾难,则在全局范围中定义了另一个PersonType:

typedef float PersonType; // just for even more trouble
/*missing: Person::*/
PersonType Person::getPersonType ()
{
    return _person_type;
}

7
我不确定这是否属于“灾难”类别:如果类型错误,则代码将无法编译。运行时错误可能会带来灾难性的后果;编译时错误,不是很多。
James McNellis 2012年

4
@JamesMcNellis比较编译器的输出:prog.cpp:13:12: error: prototype for 'PersonType Person::getPersonType()' does not match any in class 'Person'vs prog.cpp:13:1: error: 'PersonType' does not name a type .至少对于我来说,编译器的第一个错误很难理解。
PiotrNycz

我个人不同意,我发现第二条消息更难阅读,我希望实现看起来像声明。
jrh
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.