I'm working on a lexical caster. See below the simplified, problematic part of the code below. The code compiles with clang and msvc, but fails to compile with gcc. It seems, that in the first argument of lexicalCast's std::enable_if
gcc tries to evaluate all operand of the bool expression before evaluating it, while clang and msvc makes optimization and ignores the evaluation of LexicalCastable<>:: value
if the std::is_same
expressions failed.
My questions would be:
Code:
#include <type_traits>
#include <string>
template<typename From, typename To>
class LexicalCastable;
template<typename To, typename From> typename
std::enable_if<
!std::is_same<std::string, To>::value
&& !std::is_same<std::string, From>::value
&& LexicalCastable<From, std::string>::value
&& LexicalCastable<std::string, To>::value,
To>::type lexicalCast(const From &from) {
return lexicalCast<To>(lexicalCast<std::string>(from));
}
template<typename From, typename To>
class LexicalCastable
{
template<typename TT>
static auto test(int)
-> decltype(lexicalCast<TT>(std::declval<From>()), std::true_type{});
template<typename>
static auto test(...)->std::false_type;
public:
static const bool value = decltype(test<To>(0))::value;
};
static_assert(!LexicalCastable<std::string, std::string>::value, "");
Errors with gcc:
prog.cpp: In substitution of ‘template<class TT> static decltype ((lexicalCast<TT>(declval<From>()), std::true_type{})) LexicalCastable<From, To>::test(int) [with TT = std::__cxx11::basic_string<char>]’:
prog.cpp:35:45: required from ‘const bool LexicalCastable<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >::value’
prog.cpp:19:3: required by substitution of ‘template<class To, class From> typename std::enable_if<(((((! std::is_constructible<To, From>::value) && (! std::is_same<std::__cxx11::basic_string<char>, To>::value)) && (! std::is_same<std::__cxx11::basic_string<char>, From>::value)) && LexicalCastable<From, std::__cxx11::basic_string<char> >::value) && LexicalCastable<std::__cxx11::basic_string<char>, To>::value), To>::type lexicalCast(const From&) [with To = std::__cxx11::basic_string<char>; From = <missing>]’
prog.cpp:29:30: required by substitution of ‘template<class TT> static decltype ((lexicalCast<TT>(declval<From>()), std::true_type{})) LexicalCastable<From, To>::test(int) [with TT = std::__cxx11::basic_string<char>]’
prog.cpp:35:45: required from ‘const bool LexicalCastable<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >::value’
prog.cpp:19:3: required by substitution of ‘template<class To, class From> typename std::enable_if<(((((! std::is_constructible<To, From>::value) && (! std::is_same<std::__cxx11::basic_string<char>, To>::value)) && (! std::is_same<std::__cxx11::basic_string<char>, From>::value)) && LexicalCastable<From, std::__cxx11::basic_string<char> >::value) && LexicalCastable<std::__cxx11::basic_string<char>, To>::value), To>::type lexicalCast(const From&) [with To = std::__cxx11::basic_string<char>; From = <missing>]’
prog.cpp:29:30: [ skipping 889 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
prog.cpp:35:45: required from ‘const bool LexicalCastable<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >::value’
prog.cpp:19:3: required by substitution of ‘template<class To, class From> typename std::enable_if<(((((! std::is_constructible<To, From>::value) && (! std::is_same<std::__cxx11::basic_string<char>, To>::value)) && (! std::is_same<std::__cxx11::basic_string<char>, From>::value)) && LexicalCastable<From, std::__cxx11::basic_string<char> >::value) && LexicalCastable<std::__cxx11::basic_string<char>, To>::value), To>::type lexicalCast(const From&) [with To = std::__cxx11::basic_string<char>; From = <missing>]’
prog.cpp:29:30: required by substitution of ‘template<class TT> static decltype ((lexicalCast<TT>(declval<From>()), std::true_type{})) LexicalCastable<From, To>::test(int) [with TT = std::__cxx11::basic_string<char>]’
prog.cpp:35:45: required from ‘const bool LexicalCastable<std::__cxx11::basic_string<char>, std::__cxx11::basic_string<char> >::value’
prog.cpp:19:3: required by substitution of ‘template<class To, class From> typename std::enable_if<(((((! std::is_constructible<To, From>::value) && (! std::is_same<std::__cxx11::basic_string<char>, To>::value)) && (! std::is_same<std::__cxx11::basic_string<char>, From>::value)) && LexicalCastable<From, std::__cxx11::basic_string<char> >::value) && LexicalCastable<std::__cxx11::basic_string<char>, To>::value), To>::type lexicalCast(const From&) [with To = std::__cxx11::basic_string<char>; From = <missing>]’
prog.cpp:40:43: required from here
prog.cpp:29:49: fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)
-> decltype(lexicalCast<TT>(std::declval<From>()), std::true_type{});
~~~~~~~~~~~~~~~~~~^~
how can I solve this problem to be able to compile this code with GCC
You might use std::conjunction
(C++17) which guaranty short-circuit
template <typename To, typename From>
typename std::enable_if<
std::conjunction<
std::negation<std::is_same<std::string, To>>,
std::negation<std::is_same<std::string, From>>,
LexicalCastable<From, std::string>
LexicalCastable<std::string, To>>::value,
To>::type
lexicalCast(const From &from)
{
return lexicalCast<To>(lexicalCast<std::string>(from));
}