Ошибка ODR в MSVC?

Эта программа печатает 1 1 вместо 1 2 при компиляции с помощью MSVC (до VS 2015).

f1.cpp:

#include <functional>

static std::function<int ()> helper() {
    struct F { int operator()() { return 1; } };
    return F();
}

std::function<int ()> f1() { return helper(); }

f2.cpp:

#include <functional>

static std::function<int ()> helper() {
    struct F { int operator()() { return 2; } };
    return F();
}

std::function<int ()> f2() { return helper(); }

main.cpp:

#include <functional>
#include <iostream>

std::function<int ()> f1();
std::function<int ()> f2();

int main() {
    std::cout << f1()() << " " << f2()() << "\n";
}

Как будто разные определения F нарушают ODR. Но разве локальные классы не должны быть разными? Интересно, что если мы заменим F лямбда-функциями, конфликта не будет.

Так это ошибка компилятора или я неправильно понимаю одно правило определения?


person Valentin Milea    schedule 05.11.2015    source источник
comment
Немного сумасшедший, что MSVC делает что-то подобное неправильно. Это даже неправильно, если вы удалите static и используете безымянное пространство имен.   -  person Simple    schedule 05.11.2015
comment
Что происходит, когда вы используете два отдельных cout вместо одного?   -  person cup    schedule 05.11.2015
comment
Уверены ли вы? В MSVC 14.0, если helper() помещается в анонимное пространство имен, конфликт исчезает.   -  person Valentin Milea    schedule 05.11.2015
comment
Нет смысла разделять couts.   -  person Valentin Milea    schedule 05.11.2015
comment
Хм. Я получаю 1 2 сейчас. Клянусь, я получал 1 1, когда писал этот комментарий. :П   -  person Simple    schedule 05.11.2015
comment
Неважно, это была моя среда сборки. Он не обнаружил, что f1.cpp и f2.cpp изменились.   -  person Simple    schedule 05.11.2015
comment
Интересно, связано ли это с агрессивным свертыванием COMDAT, вы можете отключить его с помощью /opt:noicf   -  person Shafik Yaghmour    schedule 05.11.2015
comment
Дайте мне знать, исправляет ли это использование /opt:noicf , это кажется натяжкой, но я видел некоторые интересные перерывы из-за этого, поэтому стоит попробовать.   -  person Shafik Yaghmour    schedule 06.11.2015
comment
Получение такого же поведения с /link /opt:noicf. Я предполагаю, что свертывание применимо только к функциям с идентичными определениями, что здесь не так.   -  person Valentin Milea    schedule 06.11.2015
comment
Если вы используете C++ ›= 11, вы можете попробовать переместить F из функции в анонимное пространство имен в ваших исходных файлах.   -  person Gambit    schedule 11.02.2017
comment
Я не думаю, что это проблема ODR.   -  person alangab    schedule 10.03.2017


Ответы (1)


Это явно ошибка в MSVC, так как все типы уникальны. Возможно, для структур, определенных внутри какой-либо функции (в данном случае helper), MSVC внутренне обрабатывает их так, как если бы они были определены как helper::F, тогда как если помощник является статическим, он должен был сделать что-то вроде этого f1_cpp::helper::F вместо этого. В результате во время компоновки компоновщик видит две встроенные функции с одинаковыми именами и объединяет их в одну.

Скорее всего, переупорядочив исходные файлы, вы можете получить 2 2, если вас не устроило 1 1 :)

person Pavel P    schedule 11.04.2017