I've got the following example:
#include <cstdint>
template<typename T>
struct a_size {
constexpr static std::size_t size() { return sizeof(T); }
};
template<typename T>
constexpr std::size_t foo(T const& t) {
constexpr auto result = a_size<decltype(t)>::size();
return result;
}
template<typename T>
constexpr std::size_t bar(T const& t) {
constexpr auto result = foo(t);
return result;
}
int main() {
constexpr auto r = bar('c');
return r;
}
When compiling with g++ (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0 everything seems to be fine. But when I compile with clang++ (Version(s): clang version 10.0.0 (https://github.com/llvm-mirror/clang.git df2d9221d77042d6b07a6025edf9e810f1801ef5) (https://github.com/llvm-mirror/llvm.git a1e2fd38b595bd636661ac7416961e029c7e1689)) I've got the following error:
main.cc:16:33: error: constexpr variable 'result' must be initialized by a constant expression
constexpr auto result = foo(t);
~~~~^~
main.cc:21:24: note: in instantiation of function template specialization 'bar<char>' requested here
constexpr auto r = bar('c');
^
1 error generated.
I observe the similar behaviour on godbolt.org with clang 8.0.0 and clang trunk, the example is working, however, with GCC both 9.2.0 and trunk.
Compiled as
clang++ -std=c++17 main.cc -o main.run
g++ -std=c++17 main.cc -o main.run
Questions
Why is it so?
Why isn't constexpr-essness transitively applied/used (for lack of a better word) throughout the whole call-chain? (I understand that the result of the call to foo(t) is for some reason not marked constexpr. The question is about why.)
Which compiler is right?
What does the standard say?
Is it a bug? And if so, whether is it in GCC/G++ or in Clang?
Why removing a reference (&) in bar(T const&) solves the problem and clang no longer produces compilation error?
Edit (16.09.2019)
Obviously removing the intermediary result variable solves the problem (as suggested by David Ledger). But this does not answer the other questions. Not to mention that some use cases may require such an intermediary variable.
Related
With gcc 10.1 and boost 1.73.0, the following code
#include <boost/bimap.hpp>
int main() {
boost::bimap<int, int> lookup;
}
fails to compile with flags -O2 --std=c++20, but will succeed with flags -O2 -std=c++17 (verified with compiler explorer).
This is possibly related to the following issue: https://github.com/boostorg/bimap/pull/15 (deprecated std::allocator<void>)
Is there some workaround I can use for now to get this code to successfully compile with --std=c++20?
The reason behind the error is not that the std::allocator<void> specialization is removed. Actually, it's still valid as the primary template with the void argument. The code fails to compile, because ::rebind is no longer part of std::allocator itself. Instead, an implementation should use std::allocator_traits<Alloc>::rebind_alloc<U>.
Fortunately, most containers usually allow to specify a custom allocator as a template argument. It's also the case for boost::bimap, where an allocator can be the third, fourth or fifth template parameter, according to docs. It defaults to std::allocator, but instead, boost::container::allocator can be used, which does not produce the error:
#include <boost/bimap.hpp>
#include <boost/container/allocator.hpp>
int main() {
boost::bimap<int, int, boost::container::allocator<int>> lookup;
}
This issue has recently been fixed in 6fba6e5. boost::bimap now properly detects if a nested ::rebind is available:
template<class A, class T, class = void>
struct allocator_rebind {
typedef typename detail::alloc_to<A, T>::type type;
};
template<class A, class T>
struct allocator_rebind<A, T,
typename detail::alloc_void<typename A::template rebind<T>::other>::type> {
typedef typename A::template rebind<T>::other type;
};
I use this code with std=c++14 and gcc7.3:
#include <iostream>
#include <string>
#include <type_traits>
#include <boost/hana/assert.hpp>
#include <boost/hana/equal.hpp>
#include <boost/hana/type.hpp>
namespace hana = boost::hana;
template<class T>
bool foo(T elem)
{
constexpr auto has_overload_to_string = hana::is_valid([](auto t) -> decltype(to_string(t)) {});
constexpr bool hasOverloadTo_string = has_overload_to_string(elem);
return hasOverloadTo_string;
}
int main()
{
std::string elem;
std::cin >> elem;
foo(elem);
}
And it works fine : demo
If now I use gcc10.1, I got this error: demo fail
prog.cc: In instantiation of 'bool foo(T) [with T = std::__cxx11::basic_string<char>]':
prog.cc:41:13: required from here
prog.cc:27:38: error: temporary of non-literal type 'foo<std::__cxx11::basic_string<char> >::<lambda(auto:1)>' in a constant expression
27 | [[maybe_unused]] constexpr auto has_overload_to_string =
| ^~~~~~~~~~~~~~~~~~~~~~
prog.cc:28:21: note: 'foo<std::__cxx11::basic_string<char> >::<lambda(auto:1)>' is not literal because:
28 | hana::is_valid([](auto t) -> decltype(to_string(t)) {});
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1plus: note: 'foo<std::__cxx11::basic_string<char> >::<lambda(auto:1)>' is a closure type, which is only literal in C++17 and later
My question is : Is gcc7.3 too permissive with C++14 and is_valid works when it shouldn't or gcc8 and more add a bug with C++14 ?
The error has nothing to do with hana::is_valid, but the lambda is not valid in a constant expression in C++14.
There is already a good language lawyer answer for this here:
https://stackoverflow.com/a/32697323/800347
Clang also consistently provides an error, so it's clear that previous versions of gcc were incorrect in allowing this.
To workaround this, simply remove the constexpr qualifier to your variable declaration.
There seem to be some difference in how template parameters are resolved for clang and gcc. Or maybe clang does not consider atan2 and pow binary operations but gcc does.
The code sample below does not make much sense on its own but recreates the issue in a minimal fashion:
#include <vector>
#include <algorithm>
#include <cmath>
#define TRANSFORM_MACRO(op,func) \
template<class T> \
std::vector<T> &trans_##op(const std::vector<T> &a, const std::vector<T> &b, std::vector<T> &dst) { \
std::transform(a.begin(), a.end(), b.begin(), dst.begin(), func); \
return dst; \
} \
template std::vector<float> &trans_##op(const std::vector<float>&, const std::vector<float>&, std::vector<float>&); \
template std::vector<double> &trans_##op(const std::vector<double>&, const std::vector<double>&, std::vector<double>&);
TRANSFORM_MACRO(arctan2, ::atan2)
int main() {
return 0;
}
Compiling this with GCC (5.4 Ubuntu; 6.0 OSX Sierra) works fine. Using clang (900.0.37) returns the following error:
/Users/alneuman/CLionProjects/temptest/main.cpp:15:1: error: no matching function for call to 'transform'
TRANSFORM_MACRO(arctan2, atan2)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/.../CLionProjects/temptest/main.cpp:9:7: note: expanded from macro 'TRANSFORM_MACRO'
std::transform(a.begin(), a.end(), b.begin(), dst.begin(), func); \
^~~~~~~~~~~~~~
/Users/.../CLionProjects/temptest/main.cpp:15:1: note: in instantiation of function template specialization 'trans_arctan2<float>' requested here
/Users/.../CLionProjects/temptest/main.cpp:12:34: note: expanded from macro 'TRANSFORM_MACRO'
template std::vector<float> &trans_##op(const std::vector<float>&, const std::vector<float>&, std::vector<float>&); \
^
<scratch space>:22:1: note: expanded from here
trans_arctan2
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/algorithm:1932:1: note: candidate template ignored: couldn't infer template argument '_BinaryOperation'
transform(_InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2,
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/algorithm:1922:1: note: candidate function template not viable: requires 4 arguments, but 5 were provided
transform(_InputIterator __first, _InputIterator __last, _OutputIterator __result, _UnaryOperation __op)
The problem seems to be that GCC uses the builtin versions of atan2 and pow which only have one definition and therefore do not need specification. Clang seems to fall back to std::atan2/pow which have multiple definitions. Clang also has a __builtin_atan2 but this cannot be used with std::transform as it has to be called directly (considering the compiler output).
Thanks to #MarcGlisse I guess I figured out what causes the different behaviour for Clang and GCC. The :: notation asks to look for a function in the global namespace. GCC has atan2 builtin with a double signature in the global namespace but Clang does not. The std version Clang is using features a double and a float signature. Therefore, when std::atan2 is being used, the compiler requires more specific information which overload should be used as std::atan2 has (at least) three overloads: float, double and long double. This can be achieved with a static cast. The float cast obviously fails when GCC's built version is used since double to float cannot be casted statically.
I went for TRANSFORM_MACRO(arctan2, static_cast<T(*)(T, T)>(std::atan2)) which resolves the ambiguity in the macro and also makes GCC and Clang use the same version of atan2.
When I try some C++11 code like following, it passed in all clang++ available to me that support C++11, but it failed to compile in g++-4.8, g++-4.9 and g++-5.0.
#include <type_traits>
#include <vector>
template <class C, class First, class Last>
struct HasInsertEnd {
template <class U>
static std::false_type Check(...);
template <class U>
static auto Check(U val)
-> decltype(val.insert(val.end(), std::declval<First>(),
std::declval<Last>()),
std::true_type{});
template <class U>
using Deduce = decltype(Check<U>(std::declval<U>()));
using type = typename Deduce<C>::type;
static constexpr bool value = type::value;
};
int main(int argc, char* argv[]) {
static_assert(!HasInsertEnd<int, int, int>::value, "...");
static_assert(HasInsertEnd<std::vector<int>, const int*, const int*>::value,
"...");
return 0;
}
g++ will report errors like:
‘Check’ was not declared in this scope
If I change the calling of Check in the Deduce to HasInsertEnd::Check, both g++ and clang++ will be happy.
I know little about dependent name lookup. The problem is, which behavior is standard?
This is a bug in GCC, and can be shown to be a bug in GCC even without deferring to the standard.
template <typename T>
struct f { typedef int type; };
template <typename T>
struct S {
template <typename U>
static f<U> f();
template <class U>
using u = typename decltype(f<U>())::type;
using t = u<T>;
};
S<int>::t main() { }
This is rejected the same way in GCC 4.7.4 and GCC 5, with "error: ‘f’ was not declared in this scope". That's just nonsense. Even if the static member function should somehow not be visible, there is still a global type by the same name that would be found instead. It gets even better, though: with that global type, you get:
test.cc: In substitution of ‘template<class T> template<class U> using u = typename decltype (f<U>())::type [with U = T; T = T]’:
test.cc:12:20: required from here
test.cc:10:36: error: ‘f’ was not declared in this scope
using u = typename decltype(f<U>())::type;
^
test.cc:10:36: note: suggested alternative:
test.cc:2:12: note: ‘f’
struct f { typedef int type; };
^
test.cc:15:13: error: ‘t’ in ‘struct S<int>’ does not name a type
S<int>::t main() { }
^
That's right, it's suggesting that f can be corrected by spelling it f.
I don't see any problem with your code, and if it isn't a known bug, I encourage you to report it. and it's been reported as a bug before.
Oddly, as noted in the comments, GCC 4.8.4 and GCC 4.9.2 do find a global f. However, if the global f is a type, then they still reject the program, with "error: missing template arguments" even though the template argument is provided, so it's still clearly a bug in GCC.
When using variadic template-template arguments, to get the number of arguments using sizeof...() GCC and Clang have different requirements. GCC requires that you fill in the template arguments for the variadic argument while Clang requires you not to. Since they both claim to be standards-compliant, one must have a bug or the standards must be ambiguous (in case they should be corrected in c++1y).
Example (full compilable example at http://ideone.com/5TWFKY):
template<template <class> class... T>
struct X
{
/* code goes here */
};
GCC (note: Z is any non-template class in this case):
static const constexpr size_t count = sizeof...(T<Z>);
Clang:
static const constexpr size_t count = sizeof...(T);
MSVC 2013 (for completeness - same as Clang w/out constexpr which is not supported):
static const size_t count = sizeof...(T);
This is a bug in gcc; it's fixed in gcc 4.9.0.
From 5.3.3 [expr.sizeof ]:
5 - The identifier in a sizeof... expression shall name a parameter pack. [...]