Специализация шаблона на элементе шаблона класса шаблона

Вероятно, это только синтаксическая проблема.

Итак, у меня есть этот шаблонный класс:

template <typename String, template<class> class Allocator>
class basic_data_object
{
  template<typename T>
  using array_container = std::vector<T, Allocator<T>>;
};

И еще один :

template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
{
};

Теперь я хочу специализовать второй T параметр с помощью внутреннего typedef первого array_container для любого заданного типа.

template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
<String, Allocator,
typename basic_data_object<String, Allocator>::template array_container<T>>
{
};

Но эта специализация, похоже, не соответствует, когда я передаю std :: vector в качестве последнего параметра.

Если я создам временный жестко запрограммированный typedef:

typedef basic_data_object<std::string, std::allocator<std::string>> data_object;

И пользуйся по специализации, все работает:

template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
<String, Allocator,
data_object::template array_container<T>>
{
};

Что я пропустил ? :)


В качестве альтернативы, каков наилучший (самый маленький / самый чистый) способ сделать эту работу?


person Drax    schedule 29.04.2014    source источник
comment
Какой компилятор вы используете?   -  person Bathsheba    schedule 29.04.2014
comment
Apple LLVM версии 5.1 (clang-503.0.40) (на основе LLVM 3.4svn) - так что в основном clang 3.4, я думаю :)   -  person Drax    schedule 29.04.2014
comment
+1; ты поставил меня в тупик. Я подозревал, что отсутствует typename, но, похоже, здесь дело обстоит не так.   -  person Bathsheba    schedule 29.04.2014
comment
Короткий код, воспроизводящий проблему, выводит специализацию только в vc ++ 2013. И gcc 4.8.1, и clang 3.4 выводит общие.   -  person Constructor    schedule 29.04.2014
comment
Похоже на невыведенный контекст, хотя я предполагаю, что компиляторы должны затем выдать предупреждение. AFAIK, параметры типа шаблона выводятся независимо, и вы не можете вывести String и Allocator из последнего аргумента.   -  person dyp    schedule 29.04.2014
comment
@dyp Но оба они также появляются как первые два аргумента, как я вижу.   -  person Constructor    schedule 29.04.2014
comment
@Constructor True, чтобы вы могли вручную сделать двухэтапную дедукцию, используя вложенный шаблон. Однако я думаю, что невыявленный контекст просто отключает вывод для третьего параметра, даже если он может быть выведен с использованием первых двух.   -  person dyp    schedule 29.04.2014
comment
Есть ли способ заставить его работать (могут быть интересны вложенные шаблоны)?   -  person Drax    schedule 29.04.2014
comment
Что ж, я сдаюсь: coliru.stacked-crooked.com/a/3670b6413e6c26be   -  person Mooing Duck    schedule 29.04.2014
comment
@MooingDuck Экземпляры шаблонов псевдонимов не рассматриваются как .. ну .. экземпляры шаблонов псевдонимов. С ними обращаются как с псевдонимами. Следовательно, AFAIK ваш is_instantiation_of не работает должным образом.   -  person dyp    schedule 30.04.2014
comment
Я не вижу проблемы ... Код вроде работает: ideone.com/qmfIMb   -  person Danvil    schedule 02.05.2014
comment
@Danvil Код компилируется, но работает не так, как ожидалось.   -  person Constructor    schedule 02.05.2014
comment
Я еще больше уменьшил проблему: удалил template <class> class Allocator отовсюду, но это все равно не удалось; таким образом, Allocator не является частью проблемы.   -  person Matthieu M.    schedule 02.05.2014
comment
Джонатан Уэйкли решил проблему, с которой вы столкнулись, по головам! Что касается поиска реального решения, я боюсь, что у нас слишком мало элементов. Я предлагаю вам задать отдельный вопрос, предоставив больше контекста и, в частности, какую проблему должны решать эти классы, почему требуется спецификация и какие классы мы можем (и не можем) изменить, чтобы помочь ты. Может быть, имея более полное представление, мы могли бы предложить более элегантное решение.   -  person Matthieu M.    schedule 02.05.2014
comment
Я забыл упомянуть, что благодаря Джонатану я полностью изменил свой ответ, включив непрофессиональную интерпретацию того, что происходит в компиляторе; Надеюсь, это должно прояснить, что здесь происходит, и, таким образом, помочь сформулировать будущие ответы, чтобы избежать тех же проблем.   -  person Matthieu M.    schedule 02.05.2014


Ответы (5)


Ответ Джонатана Уэйкли объясняет причину, по которой ваш код не работает.

Мой ответ покажет вам, как решить эту проблему.


В вашем примере тип контейнера, на котором вы хотите специализироваться, определен вне basic_data_object, поэтому вы, конечно, можете использовать его непосредственно в своей специализации:

template <typename S, template<class> class A, typename T>
struct get_data_object_value<S,A,std::vector<T,A>>
{ };

Это определенно соответствует стандарту и работает со всеми компиляторами.


В случае, когда тип определен в basic_data_object, вы можете переместить его из класса.

Пример: вместо

template<typename S, template<class> class A>
struct a_data_object
{
    template<typename T>
    struct a_container
    { };
};

напишите это:

template<typename S, template<class> class A, typename T>
// you can perhaps drop S and A if not needed...
struct a_container
{ };

template<typename S, template<class> class A, typename T>
struct a_data_object
{
    // use a_container<S,A,T>
};

Теперь вы можете специализироваться на:

template <typename S, template<class> class A, typename T>
struct get_data_object_value<S,A,a_container<S,A,T>>
{ };

Примечание. Следующее «решение», по-видимому, связано с ошибкой GCC 4.8.1.

Если контейнер определен только во включающем шаблоне и не может быть перемещен, вы можете сделать это:

  1. Получить тип контейнера из basic_data_object:

    template<typename S, template<class> class A, typename T>
    using bdo_container = basic_data_object<S,A>::array_container<T>;
    
  2. Напишите специализацию для этого типа:

    template <typename S, template<class> class A, typename T>
    struct get_data_object_value<S,A,bdo_container<S,A,T>>
    { };
    
person Danvil    schedule 02.05.2014
comment
basic_data_object<S,A>::array_container<T> должно быть typename basic_data_object<S,A>::template array_container<T>, не так ли? - person Constructor; 02.05.2014
comment
@Constructor: Ну что ж, теперь, я полагаю, он вернулся к копанию в стандарте. - person Danvil; 02.05.2014
comment
Интересный обходной путь; к сожалению, просто aliasing не вытащит вас из этого, потому что псевдонимы здесь просто сахарный налет, дедукция / разрешение по-прежнему происходит на базовых типах. - person Matthieu M.; 02.05.2014
comment
Кстати, ваш предыдущий код не компилируется с clang 3.4 на coliru.stacked-crooked.com . - person Constructor; 02.05.2014
comment
@Constructor: GCC, похоже, не понимает, когда требуются шаблон и typedef ... - person Danvil; 03.05.2014
comment
Полагаю, вы имели в виду a_container вместо a_data_object в третьем примере кода второй части? - person Drax; 09.05.2014
comment
@Drax: Спасибо, что заметили ошибку! Я починил это. - person Danvil; 09.05.2014

В стандарте C ++ в параграфе 2 [temp.class.spec.match] говорится:

Частичная специализация соответствует заданному фактическому списку аргументов шаблона, если аргументы шаблона частичной специализации могут быть выведены из фактического списка аргументов шаблона (14.8.2).

14.8.2 - это [temp.arg.deduct], т.е. предложение, описывающее вывод аргументов шаблона для шаблонов функций.

Если вы измените свой код для использования аналогичного шаблона функции и попытаетесь вызвать его, вы увидите, что аргументы не могут быть выведены:

template <typename String, typename T>
void deduction_test(String,
                    typename basic_data_object<String, std::allocator>::template array_container<T>)
{ }

int main()
{
  deduction_test(std::string{}, std::vector<int, std::allocator<int>>{});
}

(Я удалил параметр Allocator, поскольку нет возможности передать параметр шаблона шаблона в качестве аргумента функции, а в типе basic_data_object это невыведенный контекст, я не верю, что это влияет на результат.)

И clang, и GCC говорят, что не могут вывести здесь T. Следовательно, частичная специализация не будет соответствовать тем же типам, которые используются в качестве аргументов шаблона.

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

В 14.8.2.5 [temp.deduct.type] мы получаем список невыведенных контекстов, которые предотвращают дедукцию, и следующее правило в параграфе 6:

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

Поскольку basic_data_object<String, Allocator> находится в невыведенном контексте (это спецификатор вложенного-имени, т.е. появляется перед ::), это означает, что тип T также не выводится, что именно об этом говорят Clang и GCC. нас.


С вашим временным жестко запрограммированным typedef нет невыведенного контекста, поэтому вывод для T выполняется успешно с использованием шаблона функции deduction_test:

template <typename String, typename T>
void deduction_test(String,
                    typename data_object::template array_container<T>)
{ }

int main()
{
  deduction_test(std::string{}, std::vector<int, std::allocator<int>>{}); // OK
}

И, соответственно, частичная специализация вашего шаблона класса может быть сопоставлена, когда он использует этот тип.


Я не вижу способа заставить его работать без изменения определения get_data_object_value, но если это вариант, вы можете избавиться от необходимости выводить тип array_container и вместо этого использовать черту, чтобы определить, является ли тип тем типом, который вы хотите, и специализируемся на результате признака:

#include <string>
#include <vector>
#include <iostream>

template <typename String, template<class> class Allocator>
class basic_data_object
{
public:
  template<typename T>
  using array_container = std::vector<T, Allocator<T>>;

  template<typename T>
    struct is_ac : std::false_type { };

  template<typename T>
    struct is_ac<array_container<T>> : std::true_type { };
};

template <typename String, template<class> class Allocator, typename T, bool = basic_data_object<String, Allocator>::template is_ac<T>::value>
struct get_data_object_value
{
};

template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value<String, Allocator, T, true>
{
  void f() { }
};

int main()
{
  get_data_object_value<std::string,std::allocator,std::vector<short>> obj;
  obj.f();
}

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

person Jonathan Wakely    schedule 02.05.2014
comment
Специализация шаблона функции работает иначе, чем специализация шаблона класса. - person Danvil; 02.05.2014
comment
@Danvil, в моем ответе не упоминается специализация шаблона функции, потому что я не говорю о специализации шаблона функции. Однако частичная специализация шаблона класса основана на выводе аргументов шаблона, который работает так же, как и для шаблонов функций. Так сказано в стандарте. - person Jonathan Wakely; 02.05.2014
comment
Да, но, как видите, дедукция для классов отличается от дедукции для функций. - person Danvil; 02.05.2014
comment
Нет, я этого не вижу. Где я должен это увидеть? Цитата, которую я привел из стандарта, даже имеет ссылку с [temp.class.spec.match] на [temp.arg.deduct]. Можете ли вы процитировать стандарт в подтверждение своего утверждения (примечание: для классов нет никаких вычетов, я тщательно подбирал слова, когда сказал, что частичная специализация шаблона класса полагается на дедукцию аргумента шаблона) - person Jonathan Wakely; 02.05.2014
comment
В [temp.class.spec] (14.5.5) есть много конкретных правил, которые применяются только к классам. Разве это не значит, что он работает по-другому? - person Danvil; 02.05.2014
comment
Единственная часть 14.5.5, которая упоминает вычет, - это 14.5.5.1, именно здесь я получил цитату в моем ответе, которая относится к 14.8.2, поэтому я не понимаю, о чем вы говорите, извините. В любом случае, мой ответ показывает прямое соответствие между совпадающими частичными специализациями и выводом аргументов для шаблонов функций: когда одна не работает, другая терпит неудачу, а когда одна успешна, другая успешна. Вы можете доказать обратное? - person Jonathan Wakely; 02.05.2014
comment
Если вы измените определение array_container на using array_container = std::vector<int>; (больше не является шаблоном, поэтому необходимы некоторые изменения), тогда код OP работает и специализация будет выбрана. Таким образом, я оспариваю утверждение невыведенного контекста, сделанное здесь - Вы можете проверить 3 упрощенных примера кода в моем ответ. - person Matthieu M.; 02.05.2014
comment
Вы блестяще объяснили, что происходит, таким образом, ответив на основную первую часть вопроса, хотя жизнеспособное решение отсутствует :). Поэтому я дождусь окончания награды, чтобы вы заработали половину. - person Drax; 09.05.2014

Почему-то проблема, кажется, связана с двойным уровнем шаблонов. Я оставлю вам 3 тестовых примера ниже, они просты:

  1. Удалите аргументы шаблона First: работает как ожидалось
  2. Сделайте First шаблон, но внутренний тип будет простым: работает должным образом
  3. Создайте как First, так и шаблоны внутреннего типа: компилируется, но результат неожиданный

Примечание: параметр шаблона шаблона Allocator бесполезен для воспроизведения проблемы, поэтому я оставил его.

Примечание: как GCC (версия ideone, 4.8.1, я полагаю), так и Clang (версия Coliru, 3.4) компилируют код, но дают один и тот же непонятный результат

Из трех приведенных выше примеров я сделал вывод:

  • , что это НЕ проблема невыводимого контекста; в противном случае почему бы (2) работать?
  • , что это НЕ проблема с псевдонимом; в противном случае почему бы (1) работать?

И поэтому то, что либо проблема гораздо более опасная, чем текущие намеки, заставит нас поверить, ИЛИ что и в gcc, и в Clang есть ошибка.

РЕДАКТИРОВАТЬ: спасибо Джонатану Уэйкли, который терпеливо обучил меня, и я наконец смог понять как стандартные формулировки, относящиеся к этому делу, так и то, как они применяются. Теперь я попытаюсь объяснить это (снова) своими словами. Пожалуйста, обратитесь к ответу Джонатана для получения точных стандартных кавычек (все они находятся в [temp.deduct.type])

  • При выводе параметров шаблона (P i), будь то для функций или классов, вывод выполняется независимо для каждого аргумента.
  • Каждый аргумент должен содержать ноль или один кандидата C i для каждого параметра; если аргумент может предоставить более одного кандидата, вместо этого он не предоставляет ни одного.
  • Таким образом, каждый аргумент создает словарь D n: P i -> C i, который отображает подмножество (возможно, пустое) параметров шаблона в быть выведенным к их кандидату.
  • The dictionaries Dn are merged together, parameter by parameter:
    • if only one dictionary has a candidate for a given parameter, then this parameter is accepted, with this candidate
    • если несколько словарей имеют одного и того же кандидата для данного параметра, то этот параметр принимается с этим кандидатом
    • если несколько словарей имеют разных несовместимых (*) кандидатов для данного параметра, то этот параметр отклоняется
  • Если окончательный словарь завершен (сопоставляет каждый параметр с принятым кандидатом), то дедукция завершается успешно, в противном случае - неуспешно.

(*) похоже, есть возможность найти "общий тип" из доступных кандидатов ... здесь это не имеет значения.

Теперь мы можем применить это к предыдущим примерам:

1) Существует единственный параметр шаблона T:

  • сопоставление шаблонов std::vector<int> с typename First::template ArrayType<T> (то есть std::vector<T>), мы получаем D 0: { T -> int }
  • объединение единственного словаря дает { T -> int }, таким образом T выводится как int

2) Существует один параметр шаблона String

  • сопоставление шаблонов std::vector<int> с String, получаем D 0: { String -> std::vector<int> }
  • сопоставление с образцом std::vector<int> против typename First<String>::ArrayType мы попадаем в невыводимый контекст (многие значения String могут соответствовать), мы получаем D 1: {}
  • объединение двух словарей дает { String -> std::vector<int> }, таким образом String получается std::vector<int>

3) Два параметра шаблона String и T существуют.

  • сопоставление шаблонов std::vector<char> с String, получаем D 0: { String -> std::vector<char> }
  • сопоставление с образцом std::vector<int> против typename First<String>::template ArrayType<T> мы попадаем в невыводимый контекст, получаем D 1: {}
  • объединение двух словарей дает { String -> std::vector<char> }, который является неполным словарем (T отсутствует) дедукция не выполняется

Должен признать, я еще не учел, что аргументы разрешаются независимо друг от друга, и поэтому, чем в этом последнем случае, при вычислении D 1 компилятор не мог воспользоваться тем фактом, что D 0 уже вычислил значение для String. Однако, почему это делается таким образом, вероятно, является самостоятельным вопросом.


Без внешнего шаблона он работает, так как печатает "Specialized":

#include <iostream>
#include <vector>

struct First {
    template <typename T>
    using ArrayType = std::vector<T>;
};

template <typename T>
struct Second {
    void go() { std::cout << "General\n"; }
};

template <typename T>
struct Second < typename First::template ArrayType<T> > {
    void go() { std::cout << "Specialized\n"; }
};

int main() {
    Second < std::vector<int> > second;
    second.go();
    return 0;
}

Без внутреннего шаблона он работает, так как печатает "Specialized":

#include <iostream>
#include <vector>

template <typename String>
struct First {
    using ArrayType = std::vector<int>;
};

template <typename String, typename T>
struct Second {
    void go() { std::cout << "General\n"; }
};

template <typename String>
struct Second < String, typename First<String>::ArrayType > {
    void go() { std::cout << "Specialized\n"; }
};


int main() {
    Second < std::vector<int>, std::vector<int> > second;
    second.go();
    return 0;
}

В обоих случаях он не работает, так как в нем выводится «Общие»:

#include <iostream>
#include <vector>

template <typename String>
struct First {
    template <typename T>
    using ArrayType = std::vector<T>;
};

template <typename String, typename T>
struct Second {
    void go() { std::cout << "General\n"; }
};

template <typename String, typename T>
struct Second < String, typename First<String>::template ArrayType<T> > {
    void go() { std::cout << "Specialized\n"; }
};

int main() {
    Second < std::vector<char>, std::vector<int> > second;
    second.go();
    return 0;
}
person Matthieu M.    schedule 02.05.2014
comment
Хорошие примеры. Однако это связано с невыведенными контекстами, см. Мой ответ. Два работающих случая не должны выводить тип из имени типа, которое включает невыведенный контекст, согласно 14.8.2.5 [temp.deduct.type] / 6. В вашем первом примере First выводить не нужно. Во втором примере First<String> - это невыведенный контекст, но ArrayType не нужно выводить. В третьем примере First<String> - это невыведенный контекст, и T необходимо вывести, но это не удается. - person Jonathan Wakely; 02.05.2014
comment
@JonathanWakely: Думаю, я начинаю понимать стандартную формулировку. Итак, по сути, проблема заключается в том, что, наивно, я ожидал, что First<String> будет выведен, как только String будет (т. Е. Я ожидал бы серийного разрешения), однако Стандарт указывает вместо этого, что каждый аргумент должен разрешаться независимо (параллельно), и только после этого следует проверять согласованность выводов (поскольку параметр шаблона может относиться только к одному типу / значению / классу шаблона). Я полагаю, для этого есть веская причина, но я этого не ожидал! - person Matthieu M.; 02.05.2014
comment
@JonathanWakely: Тем не менее, если это действительно так, я не понимаю, почему работает case (2). Если аргументы действительно разрешаются независимо, то как можно правильно вывести typename First<String>::ArrayType? Или это потому, что любое значение String даст правильный тип (исключая специализации), тогда оно совместимо со значением String, выведенным из первого аргумента, когда придет время для объединения? - person Matthieu M.; 02.05.2014
comment
@MatthieM. Компилятору нужно вывести аргументы шаблона только для каждого параметра шаблона, а не для каждого типа в списке аргументов шаблона. Для случая (1) он должен вывести T, поэтому, если создается экземпляр Second<String::ArrayType<X>>, он может вывести T как X. В случае (2) ему нужно только вывести String, потому что это единственный параметр шаблона, поэтому, если создается экземпляр Second<X, Y>, он выводит String как X, а затем заменяет его на typename First<String>::ArrayType, который формирует допустимый тип, а затем, если Y равно First<X>::ArrayType, специализация совпадает и используется. - person Jonathan Wakely; 02.05.2014
comment
В (3) он должен вывести оба String и T, но, как вы говорите, вывод каждого аргумента происходит независимо, а не слева направо. В имени типа First<String>::template ArrayType<T> есть параметр в невыведенном контексте, поэтому никакие типы не могут быть выведены из него вообще (если я правильно читаю стандарт, что не гарантируется!) String можно вывести из первого аргумента, но это все еще оставляет T невыведенным, поэтому дедукция не выполняется. - person Jonathan Wakely; 02.05.2014
comment
@JonathanWakely: Ах! Это клинч. Я потерял из виду то, что мы пытались здесь вывести. Большое спасибо за ваше тщательное наблюдение. - person Matthieu M.; 02.05.2014
comment
Это очень хорошо подведено и простой, но подробный обзор того, что происходит, спасибо :) - person Drax; 09.05.2014
comment
@Drax: Спасибо тебе тоже, это было довольно полезное упражнение для меня, и я кое-что узнал в процессе. - person Matthieu M.; 09.05.2014

В качестве альтернативы, каков наилучший (самый маленький / самый чистый) способ сделать эту работу?

Возможно, это:

  • Напишите шаблон признака SFINAE Tr<String,Allocator,T>, который определяет, совпадает ли T с basic_data_object<String,Allocator>::array_container<T::E> для некоторого типа E - если он есть - то есть T::value_type.
  • Предоставьте шаблон get_data_object_value с 4-м параметром, по умолчанию равным Tr<String,Allocator,T>::value
  • Напишите частичные специализации get_data_object_value, инстанцирующие этот 4-й параметр как true, false соответственно.

Вот демонстрационная программа:

#include <type_traits>
#include <vector>
#include <iostream>

template <typename String, template<class> class Allocator>
struct basic_data_object
{
    template<typename T>
    using array_container = std::vector<T, Allocator<T>>;
};

template<typename T, typename String, template<class> class Allocator>
struct is_basic_data_object_array_container
/* 
    A trait template that has a `static const bool` member `value` equal to
    `true` if and only if parameter type `T` is a container type
    with `value_type E` s.t. 
        `T` = `basic_data_object<String,Allocator>::array_container<T::E>`
*/ 
{
    template<typename A> 
    static constexpr bool
    test(std::is_same<
            A,
            typename basic_data_object<String,Allocator>::template
                array_container<typename A::value_type>
            > *) {
        return std::is_same<
            A,
            typename basic_data_object<String,Allocator>::template
                array_container<typename A::value_type>
            >::value;
    }
    template<typename A> 
    static constexpr bool test(...) {
        return false;
    }
    static const bool value = test<T>(nullptr);
};


template <
    typename String, 
    template<class> class Allocator,
    typename T,
    bool Select = 
        is_basic_data_object_array_container<T,String,Allocator>::value
>           
struct get_data_object_value;


template <
    typename String, 
    template<class> class Allocator,
    typename T
>           
struct get_data_object_value<
    String,
    Allocator,
    T,
    false
>
{
    static void demo() {
        std::cout << "Is NOT a basic_data_object array_container" << std::endl;
    }
};

template <
    typename String, 
    template<class> class Allocator,
    typename T>
struct get_data_object_value<
    String, 
    Allocator,
    T,
    true
>
{
    static void demo() {
        std::cout << "Is a basic_data_object array_container" << std::endl;
    }
};

#include <list>
#include <memory>

using namespace std;

int main(int argc, char **argv)
{
    get_data_object_value<string,allocator,std::vector<short>>::demo();
    get_data_object_value<string,allocator,std::list<short>>::demo();
    get_data_object_value<string,allocator,short>::demo();
    return 0;
}

Построен с использованием gcc 4.8.2, clang 3.4. Вывод:

Is a basic_data_object array_container
Is NOT a basic_data_object array_container
Is NOT a basic_data_object array_container

VC ++ 2013 не скомпилирует это из-за отсутствия поддержки constexpr. Чтобы приспособить этот компилятор, может использоваться следующая менее естественная реализация признака:

template<typename T, typename String, template<class> class Allocator>
struct is_basic_data_object_array_container
{
    template<typename A>
    static
    auto test(
        std::is_same<
            A,
            typename basic_data_object<String, Allocator>::template
                array_container<typename A::value_type>
            > *
        ) ->
            std::integral_constant<
                bool,
                std::is_same<
                    A,
                    typename basic_data_object<String, Allocator>::template
                        array_container<typename A::value_type>
                >::value
            >{}
    template<typename A>
    static std::false_type test(...);
    using type = decltype(test<T>(nullptr));
    static const bool value = type::value;
};
person Mike Kinghan    schedule 03.05.2014
comment
@Danvil Спасибо, но не вижу моего пути туда; сделал что-то еще. - person Mike Kinghan; 03.05.2014

Изменить: этот ответ работает только из-за ошибки в GCC 4.8.1.


Ваш код работает должным образом, если вы опустите ключевое слово template в своей специализации:

template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
{
    void foo() { std::cout << "general" << std::endl; }
};

template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
<String, Allocator,
typename basic_data_object<String, Allocator>::array_container<T>>
//                                         ^^^^^^ no template!
{
    void foo() { std::cout << "special" << std::endl; }
};

Пример, протестированный с GCC 4.8.1:

int main()  {
    get_data_object_value<std::string,std::allocator,std::vector<int>> obj;
    obj.foo(); // prints "special"
}
person Danvil    schedule 02.05.2014
comment
Этот код недопустим. template ключевое слово не может быть здесь опущено. И проблема не в этом. - person Constructor; 02.05.2014
comment
@Constructor: укажите соответствующий раздел в стандарте. - person Danvil; 02.05.2014
comment
Однако @Danvil clang 3.4 отклоняет его. - person Mike Kinghan; 02.05.2014
comment
@MikeKinghan: Это печально. Я до сих пор не нашел хорошей цитаты из стандарта, в которой говорится, запрещено это или разрешено ... - person Danvil; 02.05.2014
comment
См. [Temp.names] 14.2 / 4. И ответы на на этот вопрос < / а>. - person Constructor; 02.05.2014
comment
Какой компилятор вы используете? - person Constructor; 02.05.2014
comment
Как уже упоминалось: GCC 4.8.1 - person Danvil; 02.05.2014
comment
@Constructor, который вы забыли определить basic_data_object. Если вы это исправите, он будет компилироваться с GCC 4.8.1 и 4.8.2, но не с 4.9.0 и новее. - person Jonathan Wakely; 02.05.2014