2016-12-07 3 views
1

После прочтения следующего blog entry at insooth.github.io я переписал код к следующему виду, и я был удивлен его результатами:Почему шаблон не рассматривается под конкретными значениями?

#include <iostream> 
#include <limits> 
#include <type_traits> 

template <unsigned d> 
using Offset = std::integral_constant<unsigned, d>; 

template <int p> 
struct Position : std::integral_constant<int, p> { 
    static constexpr auto max = std::numeric_limits<int>::max(); 

    template <unsigned i> 
    constexpr auto operator+(Offset<i>) 
     // assertion is equivalent to: value + i <= max 
     -> std::enable_if_t<(i <= Position::max - Position::value), Position<Position::value + i>> { 
    return Position<Position::value + i>{}; 
    } 
}; 

int main() { 
    { 
    auto p = Position<11>{} + 1; 
    static_assert(std::is_same<decltype(p), int>::value, ""); 
    } 

    { 
    auto p = Position<std::numeric_limits<int>::max()>{}; 
    static_assert(std::is_same<decltype(p), Position<2147483647>>::value, ""); 
    } 

    // this will fail 
    // auto poverflow = Position<std::numeric_limits<int>::max() + 1>{}; 

    { 
    auto p = Position<std::numeric_limits<int>::max()>{} + Offset<0>{}; // OK 
    static_assert(std::is_same<decltype(p), Position<2147483647>>::value, ""); 
    std::cout << p << std::endl; 
    } 

    { 
    // 
    // MARKED 
    // 
    auto p = Position<std::numeric_limits<int>::max()>{} + Offset<1>{}; // OK but shouldn't 
    static_assert(std::is_same<decltype(p), unsigned int>::value, ""); 
    std::cout << p << std::endl; 
    } 

    { 
    // compiles ok with clang but fails with gcc 
    auto p = Position<std::numeric_limits<int>::min()>{} + 
     Offset<std::numeric_limits<unsigned>::max()>{}; // OK but wrong type 
    static_assert(std::is_same<decltype(p), unsigned int>::value, ""); 
    std::cout << p << std::endl; 
    } 
} 

Последний «тест» компилирует нормально с лязгом 3.9, но не с помощью GCC 6.2.

демо: http://coliru.stacked-crooked.com/a/61a5bf3040afaadb

Может кто-то объяснить, почему отмеченная линия

static_assert(std::is_same<decltype(p), unsigned int>::value, ""); 

компилирует -> почему p «S тип unsigned int?

РЕДАКТИРОВАТЬ

После того, как небольшое изменение, как @hvd предложил, GCC компилирует (плохо) и лязг отклоняет его.

Код:

#include <iostream> 
#include <limits> 
#include <type_traits> 

template <unsigned d> 
struct Offset 
{ 
    static constexpr unsigned value = d; 
}; 

template <int p> 
struct Position : std::integral_constant<int, p> 
{ 
    static constexpr auto max = std::numeric_limits<int>::max(); 

    template <unsigned i> 
    constexpr auto operator+(Offset<i>) 
     // assertion is equivalent to: value + i <= max 
     -> std::enable_if_t<(i <= Position::max - Position::value), Position<Position::value + i>> 
    { 
    return Position<Position::value + i>{}; 
    } 
}; 

int main() 
{ 
    { 
    auto p = Position<11>{} + 1; 
    static_assert(std::is_same<decltype(p), int>::value, ""); 
    } 

    { 
    auto p = Position<11>{} + Offset<1>{}; 
    static_assert(std::is_same<decltype(p), Position<12>>::value, ""); 
    } 

    { 
    auto p = Position<std::numeric_limits<int>::max()>{}; 
    static_assert(std::is_same<decltype(p), Position<2147483647>>::value, ""); 
    } 

    auto poverflow = Position<std::numeric_limits<int>::max() + 0>{}; 
    // this would fail 
    // auto poverflow = Position<std::numeric_limits<int>::max() + 1>{}; 

    { 
    auto p = Position<std::numeric_limits<int>::max()>{} + Offset<0>{}; // OK now 
    static_assert(std::is_same<decltype(p), Position<2147483647>>::value, ""); 
    static_assert(!std::is_same<decltype(p), unsigned int>::value, ""); 
    std::cout << p << std::endl; 
    } 

    { 
    auto p = Position<std::numeric_limits<int>::min()>{} + Offset<0>{}; // OK but wrong type 
    static_assert(!std::is_same<decltype(p), unsigned int>::value, ""); 
    std::cout << p << std::endl; 
    } 
} 

Выход:

g++ 


2147483647 
-2147483648 


clang 


In file included from main.cpp:1: 
In file included from /usr/include/c++/v1/iostream:38: 
In file included from /usr/include/c++/v1/ios:216: 
In file included from /usr/include/c++/v1/__locale:15: 
/usr/include/c++/v1/string:1938:44: error: 'basic_string<_CharT, _Traits, _Allocator>' is missing exception specification 'noexcept(is_nothrow_copy_constructible<allocator_type>::value)' 
basic_string<_CharT, _Traits, _Allocator>::basic_string(const allocator_type& __a) 
             ^
/usr/include/c++/v1/string:1326:40: note: previous declaration is here 
    _LIBCPP_INLINE_VISIBILITY explicit basic_string(const allocator_type& __a) 
            ^
main.cpp:57:58: error: invalid operands to binary expression ('Position<std::numeric_limits<int>::min()>' and 'Offset<0>') 
    auto p = Position<std::numeric_limits<int>::min()>{} + Offset<0>{}; // OK but wrong type 
      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~ 
main.cpp:20:18: note: candidate template ignored: substitution failure [with i = 0]: non-type template argument evaluates to 2147483648, which cannot be narrowed to type 'int' 
    constexpr auto operator+(Offset<i>) 
       ^
/usr/include/c++/v1/iterator:640:1: note: candidate template ignored: could not match 'reverse_iterator' against 'Offset' 
operator+(typename reverse_iterator<_Iter>::difference_type __n, const reverse_iterator<_Iter>& __x) 
^ 
/usr/include/c++/v1/iterator:1044:1: note: candidate template ignored: could not match 'move_iterator' against 'Offset' 
operator+(typename move_iterator<_Iter>::difference_type __n, const move_iterator<_Iter>& __x) 
^ 
/usr/include/c++/v1/iterator:1400:1: note: candidate template ignored: could not match '__wrap_iter' against 'Offset' 
operator+(typename __wrap_iter<_Iter>::difference_type __n, 
^ 
/usr/include/c++/v1/string:3946:1: note: candidate template ignored: could not match 'basic_string' against 'Position' 
operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs, 
^ 
/usr/include/c++/v1/string:3959:1: note: candidate template ignored: could not match 'const _CharT *' against 'Position<std::numeric_limits<int>::min()>' 
operator+(const _CharT* __lhs , const basic_string<_CharT,_Traits,_Allocator>& __rhs) 
^ 
/usr/include/c++/v1/string:3971:1: note: candidate template ignored: could not match 'basic_string' against 'Offset' 
operator+(_CharT __lhs, const basic_string<_CharT,_Traits,_Allocator>& __rhs) 
^ 
/usr/include/c++/v1/string:3982:1: note: candidate template ignored: could not match 'basic_string' against 'Position' 
operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs, const _CharT* __rhs) 
^ 
/usr/include/c++/v1/string:3994:1: note: candidate template ignored: could not match 'basic_string' against 'Position' 
operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs, _CharT __rhs) 
^ 
/usr/include/c++/v1/string:4008:1: note: candidate template ignored: could not match 'basic_string' against 'Position' 
operator+(basic_string<_CharT, _Traits, _Allocator>&& __lhs, const basic_string<_CharT, _Traits, _Allocator>& __rhs) 
^ 
/usr/include/c++/v1/string:4016:1: note: candidate template ignored: could not match 'basic_string' against 'Position' 
operator+(const basic_string<_CharT, _Traits, _Allocator>& __lhs, basic_string<_CharT, _Traits, _Allocator>&& __rhs) 
^ 
/usr/include/c++/v1/string:4024:1: note: candidate template ignored: could not match 'basic_string' against 'Position' 
operator+(basic_string<_CharT, _Traits, _Allocator>&& __lhs, basic_string<_CharT, _Traits, _Allocator>&& __rhs) 
^ 
/usr/include/c++/v1/string:4032:1: note: candidate template ignored: could not match 'const _CharT *' against 'Position<std::numeric_limits<int>::min()>' 
operator+(const _CharT* __lhs , basic_string<_CharT,_Traits,_Allocator>&& __rhs) 
^ 
/usr/include/c++/v1/string:4040:1: note: candidate template ignored: could not match 'basic_string' against 'Offset' 
operator+(_CharT __lhs, basic_string<_CharT,_Traits,_Allocator>&& __rhs) 
^ 
/usr/include/c++/v1/string:4049:1: note: candidate template ignored: could not match 'basic_string' against 'Position' 
operator+(basic_string<_CharT, _Traits, _Allocator>&& __lhs, const _CharT* __rhs) 
^ 
/usr/include/c++/v1/string:4057:1: note: candidate template ignored: could not match 'basic_string' against 'Position' 
operator+(basic_string<_CharT, _Traits, _Allocator>&& __lhs, _CharT __rhs) 
^ 
2 errors generated. 
+0

@skypjack В коде есть комментарий. Пожалуйста, отметьте это – Patryk

+0

Да, я имел в виду, какую версию. ;-) – skypjack

ответ

3

std::integral_constant<T, v> поддерживает неявное преобразование в T, получая v.

Вашего заказа operator+ терпит неудачу во время шаблона подстановки аргументов, как вы хотели из-за ваш std::enable_if_t, но, учитывая, что оба LHS Position<std::numeric_limits<int>::max()>{} и RHS поддержки Offset<1>{} неявные преобразования для встроенных типов, встроенный + оператора может быть использован язык. LHS преобразуется в int, RHS преобразуется в unsigned, и результат получает тип int + unsigned, который составляет unsigned.

+0

Можем ли мы как-то предотвратить это неявное преобразование? – Patryk

+1

@Patryk: Конечно - не выводить 'Offset ' из 'std :: integ_constant'. –

+0

@Patryk Я думаю, что это должен был быть отдельный вопрос, но 'Position :: value + i' проблематичен, поскольку это тоже' int + unsigned', что означает 'unsigned'. Ваше 'Position :: value' отрицательно и' i == 0', поэтому результат будет за пределами диапазона 'int', что делает преобразование обратно в' int' сужающим преобразованием, которое недопустимо внутри '{ } '. Это может быть сложно получить, если вы хотите поддерживать смещения больше, чем 'INT_MAX'. Я подозреваю, что вам нужно преобразовать значение смещения в 'int', и если оно находится вне диапазона' int', разделите его на несколько меньших дополнений. – hvd

Смежные вопросы