The following code (originally from Boost) fails to compile using nvcc 7.0 with C++11 support enabled:
#include <memory>
template<typename T>
struct result_of_always_void
{
typedef void type;
};
template<typename F, typename Enable = void> struct cpp0x_result_of_impl {};
template<typename F,typename T0>
struct cpp0x_result_of_impl<F(T0), typename result_of_always_void< decltype(std::declval<F>()(std::declval<T0 >()))>::type >
{
typedef decltype(std::declval<F>()(std::declval<T0 >())) type;
};
int main ()
{
return 0;
}
The error I get is the following:
test.cu:16:93: error: invalid use of qualified-name ‘std::allocator_traits<_Alloc>::propagate_on_container_swap’
typedef decltype(std::declval<F>()(std::declval<T0 >())) type;
^
I suspect this is due to a bug in the nvcc compiler, but before I file a bug, I wanted to ask if it is possible to simplify the code further while still having it produce the error?
The issue appears to be fixed in cuda 7.5RC. Please switch to the newer cuda version.
$ cat t877.cu
#include <memory>
template<typename T>
struct result_of_always_void
{
typedef void type;
};
template<typename F, typename Enable = void> struct cpp0x_result_of_impl {};
template<typename F,typename T0>
struct cpp0x_result_of_impl<F(T0), typename result_of_always_void< decltype(std::declval<F>()(std::declval<T0 >()))>::type >
{
typedef decltype(std::declval<F>()(std::declval<T0 >())) type;
};
int main ()
{
return 0;
}
$ /usr/local/cuda-7.0/bin/nvcc --version
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2015 NVIDIA Corporation
Built on Mon_Feb_16_22:59:02_CST_2015
Cuda compilation tools, release 7.0, V7.0.27
$ /usr/local/cuda-7.0/bin/nvcc -std=c++11 t877.cu -o t877
t877.cu:14:93: error: invalid use of qualified-name âstd::allocator_traits<_Alloc>::propagate_on_container_swapâ
typedef decltype(std::declval<F>()(std::declval<T0 >())) type;
^
$ nvcc --version
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2015 NVIDIA Corporation
Built on Thu_May__7_00:35:41_CDT_2015
Cuda compilation tools, release 7.5, V7.5.6
$ nvcc -std=c++11 t877.cu -o t877
$
You're welcome to file a bug, of course.
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'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.
I noticed that the following code compiles with g++/clang++-3.8 but not with nvcc:
#include <tuple> // not used, just to make sure that we have c++11
#include <stdio.h>
namespace a {
template<class T>
class X {
friend T;
};
}
I get the following compile error:
/usr/local/cuda-8.0/bin/nvcc -std=c++11 minimum_cuda_test.cu
nvcc warning : The 'compute_20', 'sm_20', and 'sm_21' architectures are deprecated, and may be removed in a future release (Use -Wno-deprecated-gpu-targets to suppress warning).
minimum_cuda_test.cu:7:10: error: ‘T’ in namespace ‘::’ does not name a type
friend T;
Interestingly, this works with nvcc:
#include <tuple> // not used, just to make sure that we have c++11
#include <stdio.h>
template<class T>
class X {
friend T;
};
Is this a bug in the compiler? I thought nvcc would internally use g++ or clang as a compiler so I am confused why this would work with my local compiler but not with nvcc.
In both cases, the code is being compiled by g++. However, when you pass a .cu file to nvcc, it puts the code through the CUDA C++ front end before passing it to the host compiler. Looking at CUDA 8 with gcc 4.8, I see that the code has been transformed from
namespace a {
template<class T>
class X {
friend T;
};
}
to
namespace a {
template< class T>
class X {
friend ::T;
};
You can see that the front end has replaced the templated friend with an equivalent, but with a prepended anonymous namespace, which is breaking the compilation. I'm not a C++ language lawyer, but this would appear to me to be a bug in the CUDA front end. I would suggest reporting it to NVIDIA.
In the hopes of using it for my needs I'm playing with std::scoped_allocator_adaptor. This minimal example uses strings and vector, but it fails to compile with on both clang++ and g++.
#include <string>
#include <iostream>
#include <vector>
#include <scoped_allocator>
struct SomeStruct
{
template <typename T>
using Allocator = std::allocator<T>;
template <typename T>
using ScopedAllocator = std::scoped_allocator_adaptor<Allocator<T>>;
using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
template <typename T>
using Vector = std::vector<T, ScopedAllocator<T>>;
SomeStruct()
: s{allocator},
v(ScopedAllocator<String>{allocator})
{
}
std::allocator<char> allocator;
String s;
Vector<String> v;
};
int main(int argc, char* argv[])
{
SomeStruct ss;
std::cout << "faffing with vector\n";
ss.v.push_back("hello"); // *** This fails to compile.
std::cout << ss.v[0] << "\n";
return 0;
}
In reality std::allocator won't be used, it's just a placeholder. This attempt was influenced by http://www.stroustrup.com/C++11FAQ.html#scoped-allocator
The compiler error message is lengthy, but boils down to the fact that a suitable constructor cannot be found.
Ubuntu clang version 3.4-1ubuntu3 (tags/RELEASE_34/final) (based on LLVM 3.4)
g++ (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4
and with C++14 at ideone.com
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.