Этот код хорошо определен? - PullRequest
8 голосов
/ 14 июля 2011

Я подозреваю, что следующая цепочка функций приведет к неопределенной последовательности в соответствии со стандартами C ++ (предположим, C ++ 0x).Просто хочу подтверждение, и если кто-нибудь может предоставить объяснение, я буду признателен.

#include <iostream>

struct TFoo 
{
    TFoo(int) 
    {
        std::cout<<"TFoo"<<std::endl;
    };
    TFoo foobar1(int) 
    {
        std::cout<<"foobar1"<<std::endl;
        return *this;
    };
    TFoo foobar2(int) 
    {
        std::cout<<"foobar2"<<std::endl;
        return *this;
    };
    static int bar1() 
    {
        std::cout<<"bar1"<<std::endl;
        return 0;
    };
    static int bar2() 
    {
        std::cout<<"bar2"<<std::endl;
        return 0;
    };
    static int bar3()
    {
        std::cout<<"bar3"<<std::endl;
        return 0;
    }
};

int main(int argc, char *argv[])
{
    // is the sequence well defined for bar1, bar2 and bar3?
    TFoo(TFoo::bar1()).foobar1(TFoo::bar2()).foobar2(TFoo::bar3());
}

* edit: удален __fastcall спецификатор для функций (не требуется / относится к вопросу).

Ответы [ 2 ]

8 голосов
/ 14 июля 2011

Порядок оценки не указан. Соответствующий раздел проекта C ++ 0x spec составляет 1,9, пункты 14 и 15:

14 Каждое вычисление значения и побочный эффект, связанный с полным выражением , упорядочивается перед каждым вычислением значения и побочным эффектом, связанным со следующим полным выражением , которое будет оценено.

15 Если не указано иное, вычисления операндов отдельных операторов и подвыражений отдельных выражений не являются последовательными.

Здесь соответствующее полное выражение :

TFoo(TFoo::bar1()).foobar1(TFoo::bar2()).foobar2(TFoo::bar3());

И поэтому оценка его подвыражений не является последовательной (если только где-то не было отмечено исключение, которое я пропустил).

Я почти уверен, что более ранние стандарты включают в себя язык, имеющий тот же эффект, но в терминах "точек последовательности".

[править]

В пункте 15 также говорится:

При вызове функции (независимо от того, является ли функция встроенной), каждое вычисление значения и побочный эффект, связанные с любым выражением аргумента или с выражением постфикса, обозначающим вызываемую функцию, упорядочиваются перед выполнением каждого выражения или оператора в тело вызываемой функции. [Примечание: вычисления значений и побочные эффекты, связанные с различными выражениями аргументов, не являются последовательными. - примечание к концу]

«Постфиксное выражение, обозначающее вызываемую функцию» - это что-то вроде foo().bar в foo().bar().

«Примечание» здесь просто поясняет, что порядок оценки аргумента не является исключением по умолчанию для «неопределенного порядка». Выводом также не является порядок оценки, связанный с «постфиксным выражением, обозначающим вызываемую функцию»; или, если хотите, порядок вычисления выражения для аргумента this. (Если бы было исключение, это было бы естественным местом для его указания. Или, возможно, в разделе 5.2.2, где говорится о вызовах функций. Ни в одном из разделов ничего не говорится о порядке оценки для этого примера, поэтому он не указан.)

1 голос
/ 14 июля 2011

Да, порядок вычисления аргументов функции не определен.

Для меня, gcc 4.5.2 на linux выдает

bar3
bar2
bar1
TFoo
foobar1
foobar2

, но clang ++ на linux и gcc 3.4.6 наСолярис производит

bar1
TFoo
bar2
foobar1
bar3
foobar2

Чтобы проанализировать более простой пример, TFoo(0).foobar1(TFoo::bar2()); - это вызов TFoo::foobar1, который принимает два аргумента: результат подвыражения TFoo(0) (в качестве скрытого аргумента this) ирезультат подвыражения Tfoo::bar2().Для меня gcc сначала выполняет bar2(), затем конструктор TFoo, а затем вызывает foobar1(), в то время как clang ++, например, сначала выполняет конструктор TFoo, затем bar2(), а затем вызывает foobar1().

...