2016-12-07 7 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でOKコンパイルしますが、GCC 6.2で失敗します。

ライブデモ:http://coliru.stacked-crooked.com/a/61a5bf3040afaadb

誰かが説明できますが、なぜ標線

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

はコンパイル - >pのタイプはunsigned intですなぜですか?

@hvdが示唆されているように小さな変化後EDIT

、(悪い)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>vを得Tへの暗黙の変換をサポートしています。あなたがためあなたのstd::enable_if_tの意図したとおりに

カスタムoperator+はテンプレート引数の置換の際に失敗しますが、組み込み型の両方LHS Position<std::numeric_limits<int>::max()>{}とRHS Offset<1>{}サポート暗黙的な変換ことを考えると、内蔵の+オペレータ言語の使用することができます。 LHSはintに変換され、RHSはunsignedに変換され、結果はint + unsignedのタイプ、つまりunsignedになります。

+0

この暗黙的な変換をどうにかすることはできますか? – Patryk

+1

@Patryk:確かに - 'std :: integral_constant'から' Offset 'を派生させないでください。 –

+0

@Patryk別の質問だったはずですが、 'Position :: value + i'も' int + unsigned'つまり 'unsigned'なので問題になります。あなたの 'Position :: value'は負で' i == 0'なので、結果は 'int'の範囲外になります。変換は' int'に変換されます。変換は '{{ } '。 'INT_MAX'よりも大きなオフセットをサポートしたい場合は、正しくするのは難しいことです。私はあなたが 'int'にオフセット値を変換しなければならないと思うし、' int'の範囲外であれば、それを複数の小さな加算に分割します。 – hvd

関連する問題