Ошибка шаблона gcc4 или более вероятная ошибка id10t - PullRequest
5 голосов
/ 27 января 2012

Следующий код прекрасно компилируется в Visual Studio, но ни gcc 4.6.2, ни 4.7 не могут его обработать.Кажется, что это правильно, но gcc не может разрешить разницу между const и не const параметрами.Может ли это быть ошибкой компилятора?

struct CReadType{};
struct CWriteType{};

template<typename ReadWriteType, typename T> 
struct AddPkgrConstByType {}; 
template<typename T> 
struct AddPkgrConstByType<CReadType, T> {
   typedef T type;
};    
template<typename T>
struct AddPkgrConstByType<CReadType, const T> {
    typedef T type;
};
template<typename T>
struct AddPkgrConstByType<CWriteType, T> {
    typedef T const type;
};

template<typename Packager, typename T>
struct AddPkgrConst : public AddPkgrConstByType<typename Packager::CReadWriteType, T> {
};

template<typename Packager, typename T>
inline bool Package( Packager* ppkgr, T* pt ) 
{
    return true;
}

template<typename Packager>
inline bool Package( Packager* ppkgr, typename AddPkgrConst<Packager,bool>::type* pb) 
{
    return false;
}

struct ReadPackager {
    typedef CReadType CReadWriteType;
};
struct WritePackager {
    typedef CWriteType CReadWriteType;
};

int main(int argc, char* argv[])
{
    ReadPackager rp;
    WritePackager wp;
    bool b = true;
    const bool cb = false;
    Package( &rp, &b );
}

Вызов компилятора:

g++ -fPIC -O -std=c++0x -Wno-deprecated -D_REENTRANT 
g++-D__STDC_LIMIT_MACROS -c test.cpp
test.cpp: In function ‘int main(int, char**)’:
test.cpp:58:22: error: call of overloaded ‘Package(ReadPackager*, bool*)’ is ambiguous
test.cpp:58:22: note: candidates are:
test.cpp:31:6: note: bool Package(Packager*, T*) [with Packager = ReadPackager, T = bool]
test.cpp:38:6: note: bool Package(Packager*, typename AddPkgrConst<Packager, bool>::type*) [with Packager = ReadPackager, typename AddPkgrConst<Packager, bool>::type = bool]

Ответы [ 3 ]

4 голосов
/ 28 января 2012

Для меня это похоже на ошибку компилятора.Здесь возникают проблемы с разрешением перегрузки и частичным упорядочением функций шаблона.Поскольку обе функции шаблона могут соответствовать списку аргументов (ReadPackager*, bool), для выбора более специализированной функции шаблона следует использовать частичное упорядочение функций шаблона.

Проще говоря, функция шаблона, по крайней мере, такая же специализированная, как и другая, еслиаргументы этой функции всегда можно использовать как аргументы другой.

Понятно, что любые два аргумента-указателя соответствуют первой функции Package (), но, например, Package (ReadPackager *, const int *) не может совпадатьвторой.Кажется, это подразумевает, что вторая функция Package является более специализированной и должна разрешать любую неоднозначность.

Однако, поскольку между компиляторами есть разногласия, могут быть некоторые тонкости, которые упускаются из виду в упрощенном объяснении.Поэтому я буду следовать процедуре определения частичного упорядочения шаблона функции из стандарта для определения правильного поведения.

Во-первых, для удобства обозначим функции как P1 и P2.

P1:

template<typename Packager, typename T>
bool Package( Packager* ppkgr, T* pt );

P2:

template<typename Packager>
bool Package( Packager* ppkgr, typename AddPkgrConst<Packager,bool>::type* pb);

Стандарт гласит, что для каждой функции шаблона (T1) мы должны генерировать уникальный уникальный тип для каждого из его параметров шаблона, использовать эти типы для определения вызова функциитипы параметров, а затем используйте эти типы для вывода типов в другом шаблоне (T2).Если это удается, первый шаблон (T1) по меньшей мере так же специализирован, как и второй (T2).Первый P2-> P1

  1. Синтезировать уникальный тип U для параметра шаблона Packager для P2.
  2. Выполнить вывод типа из списка параметров P1.Packager определяется как U, а T определяется как AddPkgrConst<Packager,U>::type.

Это успешно выполняется, и P1 считается не более специализированным, чем P2.

Теперь P1-> P2:

  1. Синтезируйте уникальные типы U1 и U2 для параметров шаблона Packager и T для P1, чтобы получить список параметров (U1 *, U2 *).
  2. Выполнить вывод типа из списка параметров P2.Packager выводится как U1.
  3. Для второго параметра вычитание не производится, поскольку, будучи зависимым типом, он считается невыводимым контекстом.
  4. Следовательно, второй аргументAddPkgrConst<U1,bool>::type, что соответствует bool.Это не соответствует второму параметру U2.

Эта процедура завершится неудачно, если мы перейдем к шагу 4. Однако я подозреваю, что компиляторы, которые отклоняют этот код, не выполняют шаг 4 и, следовательно,Рассмотрим P2 не более специализированно, чем P1 только потому, что вывод типа завершился успешноЭто кажется противоречивым, поскольку P1 явно принимает любой вход, который делает P2, а не наоборот.Эта часть стандарта несколько запутанная, поэтому неясно, нужно ли проводить это окончательное сравнение.

Попробуем ответить на этот вопрос, применив §14.8.2.5, абзац 1, Шаблон дедукцииаргументы типа

Аргументы шаблона могут быть выведены в нескольких различных контекстах, но в каждом случае тип, указанный в параметрах шаблона (назовите его P), сравнивается с фактическимtype (назовите его A), и делается попытка найти значения аргумента шаблона (тип для параметра типа, значение для параметра не типового типа или шаблон для параметра шаблона), который будет равен P после заменывыведенные значения (назовите его выведенным A), совместимым с A.

В нашем типе вычетов, выведенный A равен AddPkgrConst<U1,bool>::type = bool.Это не совместимо с оригинальным А, который является уникальным типом U2.Это, кажется, подтверждает позицию, что частичное упорядочение разрешает неоднозначность.

3 голосов
/ 27 января 2012

Я не знаю, что не так с Visual Studio, но то, что говорит gcc, кажется правильным:

Вы создаете экземпляр AddPkgrConstByType<CReadType, T>, потому что Packager::CReadWriteType разрешается в CReadType.Следовательно, AddPkgrConst<Packager,bool>::type разрешается в соответствии с первой реализацией (которая не является специализацией) в bool.Это означает, что у вас есть две отдельные функции-специализации с одним и тем же списком параметров, что C ++ не позволяет вам.

1 голос
/ 27 января 2012

Поскольку шаблоны функций не могут быть специализированными, у вас есть две перегрузки шаблонов функций.Обе эти перегрузки способны принимать bool* в качестве второго аргумента, поэтому они, по-видимому, должным образом определяются как неопределенные с помощью g ++.

Однако классы могут быть частично специализированными, так что только одинверсия будет выбрана, и вы можете использовать магию оболочки, чтобы достичь желаемой цели.Я только вставляю код, который добавил.

template <typename Packager, typename T>
struct Wrapper
{
    static bool Package()
    {
        return true;
    }
};

template <typename Packager>
struct Wrapper<Packager, typename AddPkgrConst<Packager,bool>::type>
{
    static bool Package()
    {
        return false;
    }
};

template <typename Packager, typename T>
Wrapper<Packager, T> make_wrapper(Packager* /*p*/, T* /*t*/)
{
    return Wrapper<Packager, T>();
}

int main()
{
    ReadPackager rp;
    bool b = true;
    std::cout << make_wrapper(&rp, &b).Package() << std::endl;  // Prints out 0.
}
...