web-dev-qa-db-ja.com

最近のC ++では、関数宣言で戻り変数を定義することは合法ですか?

CodeSignal でC++文法の奇妙な部分を見つけました:

string r, longestDigitsPrefix(string s)
{
   for(auto const c : s)
   {
      if(isdigit(c))
        r += c;
      else
        break;
   }
   return r;
}

最初の行は、関数宣言の前にstring rを定義しています。これは最新のC++で有効ですか?

上記のコードは、CodeSignalコンソールですべてのテストをコンパイルしてパスしますが、ローカルでコンパイルしようとするとコンパイラエラーが発生しました(--std=c++14)。

これは最新のC++で有効な文法ですか?もしそうなら、それはどの標準改訂に準拠していますか?

40
Harry

ええ、C++の文法は奇妙です。基本的に、declarations(および宣言のみ)に関しては、次のようになります。

_T D1, D2, ... ,Dn;
_

[dcl.dcl]/ )を意味します:

_T D1;
T D2;
...
T Dn;
_

これは通常のケースではおなじみでしょう:

_int a, b; // declares two ints
_

そしておそらくあなたが心配するように言われた場合には:

_int* a, b, *c; // a and c are pointers to int, b is just an int
_

しかし、宣言者は他のものも導入することができます:

_int *a, b[10], (*c)[10], d(int);
_

ここで、aはintへのポインター、bは10の配列ints、cは10の配列へのポインターints、およびdintを取り、intを返す関数です。


ただし、これはonlyが宣言に適用されます。したがって、この:

_string r, longestDigitsPrefix(string s);
_

rstringとして宣言し、longestDigitsPrefixstringを取り、stringを返す関数として宣言する有効なC++宣言です。

でもこれは:

_string r, longestDigitsPrefix(string s) { return s; }
_

無効なC++です。関数定義には独自の文法があり、init-declarator-listの一部として表示できません。

グローバル変数を使用して状態を追跡しているため、その関数の定義も不適切です。したがって、それが有効であっても、longestDigitsPrefix("12c")は最初は_"12"_を返しますが、2回目は_"1212"_を返します...

54
Barry

ISO C++ 14ドラフトN4140附属書A [グラム]を読むことで、翻訳単位から文法を推測する方法が見つからないので、間違いだと思います。

翻訳単位->宣言-シーケンス->宣言->ブロック宣言|関数定義|リンケージ仕様| ...

function-definition:attribute-specifier-seqopt decl-specifier-seqopt declarator virt-specifier-seqopt function-body

宣言子:ptr-declarator noptr-declarator parameters-and-qualifiers trailing-return-type

しかし、あなたの行はもっとコンマ演算子ですが、その文法は次のとおりです:

式:割り当て式|式、代入式

割り当て式:条件式|論理OR式|代入演算子|初期化子節|スロー式

そして、assignment-expressionからfunction-definitionへの道はありません

更新:バリーのおかげで、テキストを解析するもう1つの方法は、init-declarator-listblock-declarationから取得できます)からfunction-definitionに取得することです。

init-declarator-list:init-declarator | init-declarator-list、init-declarator

init-declarator:宣言子のinitializeropt

そして

宣言子:ptr-declarator noptr-declarator parameters-and-qualifiers trailing-return-type

関数宣言はできますが、定義はできません。したがって、この奇妙なコードは合法です:

#include <string>
using std::string;

string r, longestDigitsPrefix(string s);

string longestDigitsPrefix(string s) {
    for(auto const c : s)
    {
        if(isdigit(c))
            r += c;
        else
            break;
    }
    return r;
}

int main(int argc, char *argv[]) {
    longestDigitsPrefix("foo");

    return 0;
}

ただし、C++の正式な文法を使用するのは慣れていないため、間違っている可能性があります。これは、文法が非常に複雑であり、些細な動作がないため、これは正常です。

4
Superlokkus