下面的代码实现了一个二分法搜索,以查找枚举中是否存在运行时整数值。因为枚举是已知的编译时间,所以代码尝试以constexpr方式生成枚举值的排序数组,并具有constexpr兼容的查找。
#include <iostream>
#include <type_traits>
// ============================
using std::cout; using std::endl;
constexpr char const * const el = "\n";
// ============================
template < typename T_, typename = typename std::enable_if<std::is_enum<T_>::value>::type >
std::ostream & operator<<( std::ostream & os, T_ const & v ) {
return os << static_cast<typename std::underlying_type<T_>::type>(v)<< " ";
}
// ============================
// Minimum support for a constexpr array because std::array::operator[] lacks constexpr
template <typename T_, size_t N_>
struct Array {
constexpr size_t size() const { return N_; }
constexpr T_& operator[]( size_t i ) { return values_[i]; }
constexpr T_ const& operator[]( size_t i ) const { return values_[i]; }
constexpr T_* begin() { return values_; }
constexpr T_* end() { return values_+N_; }
constexpr T_ const * begin() const { return values_; }
constexpr T_ const * end() const { return values_+N_; }
T_ values_[N_];
};
// ============================
// return a sorted copy of the array argument
template <typename T_, size_t N_>
constexpr Array<T_, N_> const_sort( Array<T_, N_> tab ) {
Array<T_, N_> result{ T_{} };
for( size_t i{}; i != N_; ++i )
result[i] = tab[i];
for( size_t i{}; i != N_-1; ++i ) {
size_t min {i};
for( size_t j{ i+1 }; j != N_; ++j ) {
if( result[j] < result[min] )
min = j;
}
if( min != i ) {
auto tmp = std::move( result[i] );
result[i] = std::move( result[min] );
result[min] = std::move( tmp );
}
}
return result;
}
// ============================
// The user has to specialize UnsortedFunc to return an Array<EnumType,N>
template <typename T_>
constexpr auto const UnsortedFunc();
template <typename T_>
constexpr const decltype(UnsortedFunc<T_>()) Unsorted = UnsortedFunc<T_>();
// ============================
template < typename T_ >
constexpr decltype( const_sort( Unsorted<T_> ) ) Sorted = const_sort( Unsorted<T_> );
// ============================
// check for existence of a matching enum member equal to an integral value.
// The user has to feed a specialization of UnsortedFunc with the enum values.
template< typename E_>
constexpr bool Contains( typename std::underlying_type<E_>::type v ) {
using T = typename std::underlying_type<E_>::type;
T min = static_cast<T>(Sorted<E_>[0]);
T max = static_cast<T>(Sorted<E_>[Sorted<E_>.size()-1]);
if ( v < min || max < v )
return false;
size_t low {}, high { Sorted<E_>.size() };
while ( low < high ) {
size_t mid = (high + low ) / 2;
if ( v < static_cast<T>(Sorted<E_>[ mid ]) ) {
high = mid;
} else {
if ( !( static_cast<T>(Sorted<E_>[ mid ]) < v ) )
return true;
low = mid+1;
}
}
return false;
}
template < typename T_ >
void Dump() {
for( auto & v : Unsorted<T_> )
cout << v << " ";
cout << el;
for( auto & v : Sorted<T_> )
std::cout << v << " ";
cout << el;
}
enum class Foo : int { a=5, b=2, c = 8, d = 1, e = 4, f = 9 };
template <> constexpr auto const UnsortedFunc<Foo>() {
return Array<Foo,6>{ Foo::a, Foo::b, Foo::c, Foo::d, Foo::e, Foo::f };
}
通过在main函数中使用此代码,我可以有很多不同的警告和错误。
那个编译和链接,但我必须首先转储两个数组,仍然有一个警告:
int main() {
cout << std::boolalpha << __VERSION__ << el;
for( auto & v : Unsorted<Foo> )
cout << v << " ";
cout << el;
for( auto & v : Sorted<Foo> )
std::cout << v << " ";
cout << el;
constexpr bool b1 = Contains<Foo>(2);
constexpr bool b2 = Contains<Foo>(10);
cout << b1 << " " << b2 << el;
for( int i{}; i != 10; ++i )
cout << i << " : " << Contains<Foo>(i) << ", ";
}
输出:
main.cpp:60:46: warning: variable 'Unsorted<type-parameter-0-0>' has internal linkage but is not defined [-Wundefined-internal]
constexpr const decltype(UnsortedFunc<T_>()) Unsorted = UnsortedFunc<T_>();
^
main.cpp:64:71: note: used here
constexpr decltype( const_sort( Unsorted<T_> ) ) Sorted = const_sort( Unsorted<T_> );
^
1 warning generated.
4.2.1 Compatible Clang 3.5 (trunk 198621)
5 2 8 1 4 9
1 2 4 5 8 9
true false
0 : false, 1 : true, 2 : true, 3 : false, 4 : true, 5 : true, 6 : false, 7 : false, 8 : true, 9 : true,
如果我删除了两个数组转储,我有编译错误,b1和b2不能再被评估为constexpr表达式:
int main() {
cout << std::boolalpha << __VERSION__ << el;
constexpr bool b1 = Contains<Foo>(2);
constexpr bool b2 = Contains<Foo>(10);
cout << b1 << " " << b2 << el;
for( int i{}; i != 10; ++i )
cout << i << " : " << Contains<Foo>(i) << ", ";
}
输出:
main.cpp:116:20: error: constexpr variable 'b1' must be initialized by a constant expression
constexpr bool b1 = Contains<Foo>(2);
^ ~~~~~~~~~~~~~~~~
main.cpp:72:28: note: subexpression not valid in a constant expression
T min = static_cast<T>(Sorted<E_>[0]);
^
main.cpp:116:25: note: in call to 'Contains(2)'
constexpr bool b1 = Contains<Foo>(2);
^
main.cpp:117:20: error: constexpr variable 'b2' must be initialized by a constant expression
constexpr bool b2 = Contains<Foo>(10);
^ ~~~~~~~~~~~~~~~~~
main.cpp:72:28: note: subexpression not valid in a constant expression
T min = static_cast<T>(Sorted<E_>[0]);
^
main.cpp:117:25: note: in call to 'Contains(10)'
constexpr bool b2 = Contains<Foo>(10);
^
2 errors generated.
最后,如果我尝试使用转储功能转储数组,链接错误也会到达:
int main() {
cout << std::boolalpha << __VERSION__ << el;
Dump<Foo>();
}
输出:
main.cpp:60:46: warning: variable 'Unsorted<type-parameter-0-0>' has internal linkage but is not defined [-Wundefined-internal]
constexpr const decltype(UnsortedFunc<T_>()) Unsorted = UnsortedFunc<T_>();
^
main.cpp:64:71: note: used here
constexpr decltype( const_sort( Unsorted<T_> ) ) Sorted = const_sort( Unsorted<T_> );
^
main.cpp:60:46: warning: variable 'Unsorted<Foo>' has internal linkage but is not defined [-Wundefined-internal]
constexpr const decltype(UnsortedFunc<T_>()) Unsorted = UnsortedFunc<T_>();
^
main.cpp:93:21: note: used here
for( auto & v : Unsorted<T_> )
^
main.cpp:64:50: warning: variable 'Sorted<Foo>' has internal linkage but is not defined [-Wundefined-internal]
constexpr decltype( const_sort( Unsorted<T_> ) ) Sorted = const_sort( Unsorted<T_> );
^
main.cpp:97:21: note: used here
for( auto & v : Sorted<T_> )
^
3 warnings generated.
/tmp/main-22b320.o: In function `void Dump<Foo>()':
main.cpp:(.text._Z4DumpI3FooEvv[_Z4DumpI3FooEvv]+0xc): undefined reference to `_ZL8UnsortedI3FooE'
main.cpp:(.text._Z4DumpI3FooEvv[_Z4DumpI3FooEvv]+0x20): undefined reference to `_ZL8UnsortedI3FooE'
main.cpp:(.text._Z4DumpI3FooEvv[_Z4DumpI3FooEvv]+0x98): undefined reference to `_ZL6SortedI3FooE'
main.cpp:(.text._Z4DumpI3FooEvv[_Z4DumpI3FooEvv]+0xb0): undefined reference to `_ZL6SortedI3FooE'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
奖励问题,在Sorted和Unsorted定义行上,如果我使用const auto而不是decltype:
main.cpp:73:49: error: member reference base type 'const auto' is not a structure or union
T max = static_cast<T>(Sorted<E_>[Sorted<E_>.size()-1]);
~~~~~~~~~~^~~~~
main.cpp:77:37: error: member reference base type 'const auto' is not a structure or union
size_t low {}, high { Sorted<E_>.size() };
~~~~~~~~~~^~~~~
main.cpp:77:20: error: variables defined in a constexpr function must be initialized
size_t low {}, high { Sorted<E_>.size() };
^
main.cpp:93:19: error: invalid range expression of type 'const auto'; no viable 'begin' function available
for( auto & v : Unsorted<T_> )
^ ~~~~~~~~
main.cpp:114:5: note: in instantiation of function template specialization 'Dump<Foo>' requested here
Dump<Foo>();
^
main.cpp:97:19: error: invalid range expression of type 'const auto'; no viable 'begin' function available
for( auto & v : Sorted<T_> )
^ ~~~~~~
5 errors generated.
答案 0 :(得分:1)
正如Jarod42所说,这与他找到的bug有关。
使用最新的clang trunk存储库(版本4.2.1兼容Clang 3.5(202594)),一切都恢复正常。我只需编写自己的constexpr std :: move,因为Visual Studio头文件还不符合C ++ 14标准。
使用该命令行构建好clang-cl.exe /GR- -D_HAS_EXCEPTIONS=0 /O0 /FA /Wall -Xclang -std=c++1y main.cpp
:
最终代码:
#include <iostream>
#include <type_traits>
using std::cout;
using std::endl;
constexpr char const * const el = "\n";
// ============================
// VS2013 do not have constexpr supports in the standard library
namespace std14 {
template< class T >
constexpr typename std::remove_reference<T>::type&& move( T&& t ) {
return static_cast<typename std::remove_reference<T>::type&&>(t);
}
}
// ============================
// Minimum support for a constexpr array because std::array::operator[] lacks constexpr
template <typename T_, size_t N_>
struct Array {
constexpr size_t size() const { return N_; }
constexpr T_& operator[]( size_t i ) { return values_[i]; }
constexpr T_ const& operator[]( size_t i ) const { return values_[i]; }
constexpr T_* begin() { return values_; }
constexpr T_* end() { return values_+N_; }
constexpr T_ const * begin() const { return values_; }
constexpr T_ const * end() const { return values_+N_; }
T_ values_[N_];
};
// ============================
// return a sorted copy of the array argument
template <typename T_, size_t N_>
constexpr Array<T_, N_> const_sort( Array<T_, N_> tab ) {
Array<T_, N_> result{ { T_{} } };
for( size_t i{}; i != N_; ++i )
result[i] = tab[i];
for( size_t i{}; i != N_-1; ++i ) {
size_t min {i};
for( size_t j{ i+1 }; j != N_; ++j ) {
if( result[j] < result[min] )
min = j;
}
if( min != i ) {
using std14::move;
auto tmp = move(result[i]);
result[i] = move(result[min]);
result[min] = move(tmp);
}
}
return result;
}
// ============================
// The user has to specialize UnsortedFunc to return an Array<EnumType,N>
template <typename T_>
constexpr auto UnsortedFunc();
template <typename T_>
constexpr const decltype(UnsortedFunc<T_>()) Unsorted = UnsortedFunc<T_>();
// ============================
template < typename T_ >
constexpr decltype( const_sort( Unsorted<T_> ) ) Sorted = const_sort( Unsorted<T_> );
// ============================
// check for existence of a matching enum member equal to an integral value.
// The user has to feed a specialization of UnsortedFunc with the enum values.
template< typename E_>
constexpr bool Contains( std::underlying_type_t<E_> v ) {
using T = std::underlying_type_t<E_>;
constexpr auto & sorted = Sorted<E_>;
T min = static_cast<T>(sorted[0]);
T max = static_cast<T>(sorted[sorted.size()-1]);
if ( v < min || max < v )
return false;
size_t low {}, high { sorted.size() };
while ( low < high ) {
size_t mid = (high + low ) / 2;
if ( v < static_cast<T>(sorted[ mid ]) ) {
high = mid;
} else {
if ( !( static_cast<T>(sorted[ mid ]) < v ) )
return true;
low = mid+1;
}
}
return false;
}
enum class Foo : int { a = 5, b = 2, c = 8, d = 1, e = 4, f = 9 };
template <> constexpr auto UnsortedFunc<Foo>() {
return Array<Foo,6>{ { Foo::a, Foo::b, Foo::c, Foo::d, Foo::e, Foo::f } };
}
int main() {
cout << std::boolalpha << __VERSION__ << el;
constexpr bool b1 = Contains<Foo>(2);
cout << b1 << el;
for( int i{}; i != 10; ++i ) {
cout << i << " : " << Contains<Foo>(i) << ", ";
}
}