使用 constexpr 模板函数时出现各种编译和链接器错误
Various compilation and linker errors while using constexpr template functions
下面的代码实现了二分搜索,以查找枚举中是否存在运行时整数值。由于枚举是编译时已知的,因此代码尝试以 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.
最后,如果我尝试使用 Dump 函数转储数组,链接错误也会到来:
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)
奖励问题,在已排序和未排序的定义行上,如果我使用 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.
正如Jarod42所说,这与他发现的错误有关。
使用最新的 clang 主干存放处(版本 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) << ", ";
}
}
相关文章:
- Visual Studio 2017 ARM 交叉编译链接器崩溃
- CGAL 编译/链接器错误
- opencv 3.0 Visual Studio Community 2017 编译链接错误
- 在 Linux 中编译/链接多个 C++ 库
- 加速极慢的 MinGW-w64 编译/链接?
- Xcode - 如何使用"-fexhaustive-register-search"进行编译 - 链接器崩溃需要
- 交叉编译:链接时对std::__atomic_futex_unsigned_base::_M_futex_notify_
- 无法从单独的.cpp使用模板化成员编译/链接类
- 交叉编译链接器错误
- Eclipse C/C++:外部库的交叉编译链接器错误:Ubuntu VM amd64 to Ubuntu armhf
- 编译/链接 C 和 C++ 文件时出现问题
- C++编译链接
- 如何正确编译/链接Linux Makefile中的单元测试
- 在以下程序中获取编译链接错误
- 与Boost日志进行交叉编译链接期间未解决的符号
- 如何编译/链接Boost与clang++/libc++
- Cef项目编译链接error2001
- 我如何编译/链接测试用例(googleTest)使用cmake的代码捆绑在共享库
- 编译链接
- 编译/链接过程是如何工作的