2016-12-02 3 views
1

Я играю с концепциями TS в новом проекте. Мой вопрос связан с кажущейся круговой зависимостью между шаблоном структуры и связанной с ней концепцией, которую я хочу создать. Конкретная логика концепции заключается в проверке того, что аргумент типа для концепции является специализацией шаблона структуры. Поскольку я хотел бы, чтобы концепция была доступна для использования внутри шаблона структуры, мне, видимо, необходимо определить концепцию до шаблона структуры, но тогда логика концепции должна также знать о шаблоне структуры. Я разработал что-то, что скомпилирует, переместив , объявив шаблоном структуры Vector, затем определив концепцию VectorSpecialization, а затем , определяющий шаблон структуры Vector. Мой конкретный вопрос связан с тем, что я использую предложение requires для шаблона структуры; когда я его объявляю, компилятор дает мне ошибку, если я не дублирую полное предложение requires. (См. Код ниже).Избегайте дублирования запроса в форвардном объявлении шаблона?

Моего конкретный вопрос является: есть способ избежать полного дублирования requires пункта между упреждающим объявлением шаблоном и определением? Одна из возможностей заключалась бы в том, чтобы исключить логику предложения requires в общую сущность, которую могут делегировать как декларация, так и определение, что, я полагаю, будет касаться принципа DRY; но мне любопытно, есть ли структурное решение более высокого уровня, которое я мог бы сделать здесь, чтобы избежать необходимости даже иметь предложение requires в обоих местах, или если есть более идиоматический способ использования концепций для подобных случаев, которые я мог бы использовать выгода от. Чтобы повторить итерацию, я использую этот вариант: написать концепцию, которая будет использоваться в шаблоне, но концепция также должна знать о шаблоне.

// Forward declare the struct template so that the concept can refer to it 
// Note the need to repeat the 'requires' clause. Can that repetition be 
// be avoided? 
template< typename T, size_t N > requires N > 1 struct Vector; 

// compile-time overload set using template arg deduction to detect 
// when the argument is a specialization of 'Vector' 
template< typename NonVector > 
    constexpr bool IsVectorSpecialization(NonVector &&) { 
     return false; 
} 
template< typename T, size_t N > 
constexpr bool IsVectorSpecialization(Vector<T, N> &&) { 
    return true; 
} 

// The concept, which uses the above overloaded constexpr function 
template< typename VectorCandidate > 
concept bool VectorSpecialization_CV 
     = IsVectorSpecialization(std::declval<VectorCandidate>()); 

template< typename T, size_t N > 
requires N > 1 
struct Vector : std::array<T, N> { 
    // Some function templates with VectorSpecialization parameters, e.g. 
    //  T dot(VectorSpecialization const &other) const; 
    // ... 
}; 

(Примечание: Помимо конкретного вопроса, я бы также приветствовать обсуждение (в комментариях, конечно) об аспектах дизайна Концепции TS, которые имеют по этому вопросу и/или решений, которые предлагают людям , так как часть причины, по которой я играю с концепцией TS, - это увидеть, насколько хорошо она работает на практике, чтобы увидеть, есть ли какая-нибудь полезная обратная связь для комитета до полной стандартизации. Например, есть ли настройка дизайна " Concepts Lite ", который мог бы устранить необходимость дублирования requires таких статей?)

+0

Есть пара проблем с образцом кода: (1) концепция называется 'VectorSpecialization_CV', но в другом месте называется« VectorSpecialization », (2) определение ODR - использует' std :: declval', что нет-нет. – Casey

+0

@ Casey, не могли бы вы рассказать о том, что 'std :: declval' является no-no? Он компилировался для меня, но это потому, что я еще не создавал шаблон функции «точка»; как только я добавил код для этого, компилятор (GCC) сказал мне «error: call to non-constexpr function», ссылаясь на 'std :: declval ()', на который вы указали. Я достаточно хорошо понимаю эту ошибку. Но есть ли что-то еще для этого, о чем вы говорили? –

+0

Это достаточно близко;) Стандарт явно запрещает ODR-использование 'declval' ([declval]/2), поскольку реализации только объявляют его. Использование ODR в контексте 'constexpr' плохо организовано по двум причинам. – Casey

ответ

3

Одна из гарантий, предоставляемых ограниченными шаблонами, заключается в том, что whenev задана спецификация шаблона, параметры должны соответствовать ограничениям. См P121R0 §14.3 [temp.names]/8:

When the template-name of a simple-template-id names a constrained non-function template or a constrained template template-parameter, but not a member template that is a member of an unknown specialization (14.7), and all template-arguments in the simple-template-id are non-dependent 14.6.2.4, the associated constraints of the constrained template shall be satisfied. (14.10.2).

В контексте вашего примера, это означает, что, например, это плохо сформированным назвать Vector<int, 1> даже без его инстанцировании:

template< typename T, size_t N > requires N > 1 struct Vector; 
using foo = Vector<int, 1>*; 
// ill-formed: constraints not satisfied: '(N > 1)' evaluated to false 

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

Это еще более очевидно в контексте шаблонов функций, где в противном случае идентичные объявления шаблонов функций с различными связанными ограничениями объявляют перегрузку. Например:

template<typename T> 
requires true 
bool foo(T) { return true; } 

template<typename T> 
requires false 
bool foo(T) { return false; } 

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

Концепции - это механизм, который предоставляет язык для управления этим повторением: вместо повторения огромных выражений ограничения снова и снова мы предоставляем им удобные имена. N > 1 вряд ли достаточно обременительный заслуживающие именованная концепция, чтобы сохранить нажатия клавиш, но providing a single point of definition for the notion is clearly worthwhile:

template< size_t N > concept bool VectorLength = N > 1; 
template< typename T, VectorLength N > struct Vector; 

template<typename> 
constexpr bool IsVectorSpecialization = false; 
template< typename T, size_t > 
constexpr bool IsVectorSpecialization<Vector<T, N>> = true; 

template< typename VC > 
concept bool VectorSpecialization = IsVectorSpecialization<VC>; 

template< typename T, VectorLength N > 
struct Vector : std::array<T, N> { 
    T dot(VectorSpecialization const&); 
}; 

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