Използване на std::enable_if на тип връщане на функцията на шаблона за използване на SFINAE - грешка при компилация

Следният код

#include <type_traits>

struct CByteArray {};
struct HLVariant {
    HLVariant() {}
    HLVariant(const HLVariant&) {}
    HLVariant(const CByteArray&) {}

    };

template <typename T>
inline typename std::enable_if<!std::is_pod<T>::value, CByteArray>::type serialize(const T& value)
{
    return serialize(HLVariant(value));
}

template <typename T>
inline typename std::enable_if<std::is_pod<T>::value, CByteArray>::type serialize(const T& value)
{
    return CByteArray();
}

template <>
inline CByteArray serialize(const HLVariant& value)
{
    return CByteArray();
}

int main()
{
    serialize(0);
    serialize(CByteArray());
    serialize(HLVariant());

    return 0;
}

задейства грешка при компилиране C2794: 'type' : is not a member of any direct or indirect base class of 'std::enable_if<false,CByteArray>' в MSVC 2013. Тя обаче работи в ideone: въведете описание на връзката тук

Каква е грешката тук?

Грешката е същата в MSVC 2010, 2012 и 2013.


person Violet Giraffe    schedule 03.11.2014    source източник
comment
Бих заложил, че грешката е MSVC 2013 :)   -  person Pradhan    schedule 03.11.2014
comment
@Pradhan: ако намекваш, че компилаторът на MS е по някакъв начин по-нисък от GCC, тогава не можеш да бъдеш по-далеч от истината.   -  person Violet Giraffe    schedule 03.11.2014
comment
@VioletGiraffe В съответствие с езика C++? Компилаторът на MS е на мили назад. Има и други предимства, но не се заблуждавайте за съответствието на езика C++. Каква актуализация на MSVC 2013?   -  person Yakk - Adam Nevraumont    schedule 03.11.2014
comment
работи добре във VS14 CTP   -  person Piotr Skotnicki    schedule 03.11.2014
comment
@Yakk: Riiight, все още не е съвместим със C++14. Все пак, може ли да се върнем към темата, моля?   -  person Violet Giraffe    schedule 03.11.2014
comment
Не, не е съвместим с C++11. Ще бъде претенциозно да се каже, че е съвместим с C++03. И така, каква актуализация на MSVC2013 използвате? (GM? U1? U2? U3? -- те се различават) На кой ред по-конкретно възниква грешката? Можете ли да елиминирате кода, който не предотвратява грешката -- трябва да се появи само с 1 претоварване на serialize, което би улеснило да разберете какво се обърка. Ако изтриете serialize, които нямат грешката, ще продължи ли да генерира грешката? Ако е така, редактирайте публикацията си с по-простия код.   -  person Yakk - Adam Nevraumont    schedule 03.11.2014
comment
@Yakk: Среща се с MSVC 2010, 2012 и 2013, така че не би трябвало да има значение коя актуализация имам (Актуализация 3, ако все още смятате, че е уместна). Мисля, че няма нищо, което бих могъл да премахна, без да премахна съответния код. Според съобщението за грешка, грешка възниква на ред 27.   -  person Violet Giraffe    schedule 03.11.2014
comment
@Yakk: честно, нека опитам. Все още не съм сигурен, че това е еквивалентна трансформация, тъй като SFINAE вече няма да се използва за резолюция на шаблон.   -  person Violet Giraffe    schedule 03.11.2014
comment
SFINAE се случва преди претоварванията да бъдат избрани от стандарта. Гледайки по-долу, template<> може да е замесен в проблема, което може да е страничен ефект от това как MSVC прави пълна специализация. Кой ред е ред 27?   -  person Yakk - Adam Nevraumont    schedule 03.11.2014
comment
@Yakk: ред 27 е затварящата скоба на template <> CByteArray serialize(const HLVariant& value) тяло. Каквото и да означава това.   -  person Violet Giraffe    schedule 03.11.2014
comment
А, мисля, че разбирам. MSVC третира това като пълна специализация на ... и двете? ... от шаблоните по-горе и по някаква причина извършва заместване обратно в оригинала и получава грешка, която третира като трудна. Като общо правило, не специализирайте функции на шаблони, специализацията на шаблони на функции се прави лошо.   -  person Yakk - Adam Nevraumont    schedule 03.11.2014
comment
@Yakk: Експериментирах с него и мога да кажа две неща със сигурност: 1. грешката възниква по време на всяко компилиране на кода на шаблона, а не само по време на инстанциране. т.е. ако премахна всички serialize повиквания, грешката остава.   -  person Violet Giraffe    schedule 03.11.2014
comment
@Yakk: 2. Компилаторът се опитва да компилира (или може би просто да анализира, не знам) всяка специализация и всяка базова неспециализирана версия, той не разбира, че се опитвам да ги скрия. С други думи: когато компилаторът срещне <> serialize (const int&) специализация, той се опитва да замени int в двете базови версии на шаблона и тъй като една от тях винаги е скрита - компилирането на 1 от bae функциите винаги ще се провали, независимо от типа, след като среща на компилатора enable_if<false, T>. Грешка в компилатора?   -  person Violet Giraffe    schedule 03.11.2014
comment
Опитва се да разбере коя функция на шаблона е специализация на вашата специализация е моето предположение и прави това чрез заместване и генериране на сериозни грешки. Не специализирайте шаблони на функции, почти винаги е лоша идея, дори ако тук нямаше грешка в MSVC.   -  person Yakk - Adam Nevraumont    schedule 03.11.2014
comment
@Yakk: как така? Защо лоша идея, искам да кажа? Ще помогне ли, ако превърна безплатните си шаблони за функции в шаблони за функции на членове на клас? Или шаблони за клас?   -  person Violet Giraffe    schedule 03.11.2014
comment
Защото специализацията реагира странно на претоварванията и често не прави това, което мислите, че трябва да прави. Специализирани класове, функции за претоварване.   -  person Yakk - Adam Nevraumont    schedule 03.11.2014
comment
@Yakk: В този случай имам нужда от шаблон, така че претоварването е изключено. И така, вие казвате, че трябва да приложа функцията serialize в шаблон на клас и да специализирам този шаблон? Какво ще кажете за специализиране на шаблонен метод на нешаблонен клас?   -  person Violet Giraffe    schedule 03.11.2014
comment
Вече използвате претоварвания -- имате две шаблонни функции, които се претоварват взаимно. Те НЕ са специализации един на друг. Ако смятате, че имате нужда от една функция на шаблон, вече сте се провалили със задачата си. Функциите на шаблона могат да се претоварват една друга добре, а функциите без шаблон могат да участват в разрешаването на претоварването с тях (внимателно).   -  person Yakk - Adam Nevraumont    schedule 03.11.2014
comment
@Yakk: Как се претоварва, ако дори може да се компилира само 1 от 2 варианта на функция? И имат абсолютно същия подпис.   -  person Violet Giraffe    schedule 03.11.2014


Отговори (1)


Изглежда добре за мен, но мога да го поправя, като премахна template<> от последното претоварване на serialize. Няма нужда да го правите пълна специализация, когато нормалното претоварване ще свърши работа!

РЕДАКТИРАНЕ: Това, което друго работи, е предоставянето на шаблонна специализация, която съвпада само с HLVariant (и допълнително ограничаване на другите специализации да не съвпадат вече с HLVariant, за да се избегне двусмислие).

Това трябва да го направи:

http://ideone.com/0UGkcn

#include <type_traits>
#include <iostream>

struct CByteArray {};
struct NonPod {public: int a; private: int b;};
struct HLVariant {
    HLVariant() {}
    HLVariant(const HLVariant&) {}
    HLVariant(const CByteArray&) {}
    HLVariant(const NonPod&) {}
};

template <typename T>
inline typename std::enable_if<std::is_same<T, HLVariant>::value && !std::is_pod<T>::value, CByteArray>::type serialize(const T& value)
{
    std::cout << "serialize non-pod variant\n";
    return CByteArray();
}

template <typename T>
inline typename std::enable_if<!std::is_same<T, HLVariant>::value && !std::is_pod<T>::value, CByteArray>::type serialize(const T& value)
{
    std::cout << "serialize non-pod non-variant\n";
    return serialize(HLVariant(value));
}

template <typename T>
inline typename std::enable_if<std::is_pod<T>::value, CByteArray>::type serialize(const T& value)
{
    std::cout << "serialize pod\n";
    return CByteArray();
}

int main()
{
    std::cout << "int:\n";
    serialize(0);
    std::cout << "CByteArray:\n";
    serialize(CByteArray());
    std::cout << "HLVariant:\n";
    serialize(HLVariant());
    std::cout << "NonPod:\n";
    serialize(NonPod());
}
person Ben Hymers    schedule 03.11.2014
comment
Виждам какво направихте там... Проблемът е, че в действителния ми код такова претоварване ще доведе до цял куп грешки, тъй като претоварванията без шаблони имат предимство (IIRC) и HLvariant може да бъде конструиран от около 50 различни типа през не -изрични конструктори. С други думи, ще бъдат извикани грешни версии на serialize. - person Violet Giraffe; 03.11.2014
comment
Дефинираното от потребителя преобразуване трябва да е по-малко добро съвпадение от точното съвпадение на шаблон? - person Yakk - Adam Nevraumont; 03.11.2014
comment
Добре, изглежда, че искате шаблонна специализация, която съответства само на HLVariant (и за да предотвратите съвпадението на другите специализации с HLVariant, за да избегнете неяснота). Ще актуализирам... - person Ben Hymers; 03.11.2014
comment
@BenHymers: да, това е точно така. И ще добавя още специализации в бъдеще. - person Violet Giraffe; 03.11.2014
comment
Значи специализацията не работи, но скриването с enable_if все още работи? Направих кратка проверка - компилира се в MSVC. - person Violet Giraffe; 03.11.2014
comment
Да, вярвам, че вашият оригинален код трябва да бъде приет и че не успява да се компилира в MSVC е грешка; това е просто заобиколно решение, което за съжаление е доста тромаво... - person Ben Hymers; 03.11.2014