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;
};
Related
My question is about template static members not about template static functions.
Prior to C++17, when building with GCC and having includes in several CPP files, to avoid "multiple definitions" linker errors of static specialized template members similarly to static functions, they have to be either defined as inline or defined in CPP files (see this).
I have reasons not to define static template members in CPP files so I have to use inline. But to do this I have to switch in both GCC and VS2017 to C++17 because static template inline functions are allowed after C++11 but static template inline members are allowed only after C++17.
But this whole problem does not exist at all in MSVC. This is demonstrated below:
ClassT.h:
template <typename T>
class ClassT
{
public:
static const T CC;
};
template<> const char ClassT<char>::CC = '\\';
Main.cpp:
#include "ClassT.h"
int main()
{
return 0;
}
Test.cpp:
#include "ClassT.h"
This will compile and links without warnings in MSVC but will not compile with GCC giving linker error:
"multiple definition of `ClassT::CC'.
MSVC linker figures out that there is more than one definition and takes only one of them while GCC obviously fails to do this. To solve it for GCC one have to add inline:
template <typename T>
class ClassT
{
public:
inline static const T CC;
};
template<> inline const char ClassT<char>::CC = '\\';
Which however forces to switch to C++17 otherwise will not compile with MSVC.
I wonder which one is correct and according to specification in this case GCC or MSVC linker and how exactly the latter resolves such static members without the inline definition?
CUDA 10.1
g++ 7.3
For the purposes of a unit testing suite, I need large sets of repeatable data (more than can be hard-coded). I came up with this "generator" paradigm, with the idea that it could be used to fill arbitrary containers with data.
Now I need to extend the generator to populate multivalue containers (float2, int2, thrust::complex). My solution is to use SFINAE to conditionally define a function based on whether it can be constructed from a single value or requires a pair of values.
The below code compiles fine with GCC (-std=c++1z) but fails with nvcc (-std=c++14)
#include <random>
#include <type_traits>
template <typename T>
class RandomGenerator
{
public:
RandomGenerator(T min, T max, unsigned seed = 42);
// Generate the next element in the sequence. This requires keeping all the necessary state
// to advance
T operator()();
// fill a container if it is scalar
template<typename Container>
typename std::enable_if_t<std::is_constructible_v<typename Container::value_type, T>>
fill( Container& c );
// fill a container if it takes 2 values (e.g., complex)
template<typename Container>
typename std::enable_if_t<std::is_constructible_v<typename Container::value_type, T, T>>
fill2( Container& c );
protected:
private:
std::uniform_real_distribution< T > dist;
std::mt19937 rng;
};
// Constructor - define the domain of this generation
template <typename T>
RandomGenerator<T>::RandomGenerator(T min, T max, unsigned seed)
: dist(min, max)
, rng()
{
rng.seed(seed);
}
// generate one random number
template <typename T>
T
RandomGenerator<T>::operator()()
{
return dist(rng);
}
template <typename T>
template<typename Container>
typename std::enable_if_t<std::is_constructible_v<typename Container::value_type, T>>
RandomGenerator<T>::fill( Container& c )
{
std::generate(c.begin(), c.end(), *this);
}
template <typename T>
template<typename Container>
typename std::enable_if_t<std::is_constructible_v<typename Container::value_type, T, T>>
RandomGenerator<T>::fill2( Container& c )
{
std::generate(c.begin(), c.end(), [this]() { return typename Container::value_type( (*this)(), (*this)() ); });
}
The compiler error:
/usr/local/cuda/bin/nvcc -g -Xcompiler=-fPIE -gencode=arch=compute_60,code=sm_60 --std=c++14 --use_fast_math -Xcompiler -pthread -x cu -dc unittest.cu -o unittest.cu.o
RandomGenerator.hh(30): error: namespace "std" has no member "is_constructible_v"
RandomGenerator.hh(30): error: type name is not allowed
RandomGenerator.hh(30): error: expected an identifier
RandomGenerator.hh(34): error: namespace "std" has no member "is_constructible_v"
RandomGenerator.hh(34): error: type name is not allowed
RandomGenerator.hh(34): error: too many arguments for alias template "std::enable_if_t"
RandomGenerator.hh(34): error: expected an identifier
RandomGenerator.hh(63): error: namespace "std" has no member "is_constructible_v"
RandomGenerator.hh(63): error: type name is not allowed
RandomGenerator.hh(63): error: expected an identifier
RandomGenerator.hh(71): error: namespace "std" has no member "is_constructible_v"
RandomGenerator.hh(71): error: type name is not allowed
RandomGenerator.hh(71): error: too many arguments for alias template "std::enable_if_t"
RandomGenerator.hh(71): error: expected a ";"
Am I missing something? Is there a way to pass this to CUDA?
UPDATE
It appears that the specific problem is with std::enable_if_t, std::is_XXX_v. If instead of these typdefs I use the more verbose form
typename std::enable_if<std::is_constructible<typename Container::value_type, T>::value>::type
then nvcc can handle it.
The compiler error gives you the hint that it does not know anything about std::is_constructible_v.
This is not due to the switch from gcc to nvcc but from c++17 to c++14.
You have already found out that you can use the ::value form instead and that's the way to go here. (Of course you could also expand the standard namespace if you plan to make c++17 a requirement for your project as soon as nvcc supports it.)
You can keep the std::enable_if_t though as that was already introduced in c++14.
More generally one can find that all std::something_t shortcuts are available from c++14 on but the std::something_v shortcuts were introduced in c++17.
In a very simple situation with a constrained constructor, testing for convertibility of the argument, an error is produced in clang, but not in g++:
#include <type_traits>
template <class T, class U>
constexpr bool Convertible = std::is_convertible<T,U>::value && std::is_convertible<U,T>::value;
template <class T>
struct A
{
template <class S, class = std::enable_if_t<Convertible<S,T>> >
A(S const&) {}
};
int main()
{
A<double> s = 1.0;
}
Maybe this issue is related to Is clang's c++11 support reliable?
The error clang gives, reads:
error: no member named 'value' in 'std::is_convertible<double, A<double> >'
constexpr bool Convertible = std::is_convertible<T,U>::value && std::is_convertible<U,T>::value;
~~~~~~~~~~~~~~~~~~~~~~~~~~^
I've tried
g++-5.4, g++-6.2 (no error)
clang++-3.5, clang++-3.8, clang++-3.9 (error)
with argument -std=c++1y and for clang either with -stdlib=libstdc++ or -stdlib=libc++.
Which compiler is correct? Is it a bug in clang or gcc? Or is the behavior for some reasons undefined and thus both compilers correct?
First of all, note that it works fine if you use:
A<double> s{1.0};
Instead, the error comes from the fact that you are doing this:
A<double> s = 1.0;
Consider the line below (extracted from the definition of Convertible):
std::is_convertible<U,T>::value
In your case, this is seen as it follows (once substitution has been performed):
std::is_convertible<double, A<double>>::value
The compiler says this clearly in the error message.
This is because a temporary A<double> is constructed from 1.0, then it is assigned to s.
Note that in your class template you have defined a (more or less) catch-all constructor, so a const A<double> & is accepted as well.
Moreover, remember that a temporary binds to a const reference.
That said, the error happens because in the context of the std::enable_if_t we have that A<double> is an incomplete type and from the standard we have this for std::is_convertible:
From and To shall be complete types [...]
See here for the working draft.
Because of that, I would say that it's an undefined behavior.
As a suggestion, you don't need to use std::enable_if_t in this case.
You don't have a set of functions from which to pick the best one up in your example.
A static_assert is just fine and error messages are nicer:
template <class S>
A(S const&) { static_assert(Convertible<S,T>, "!"); }
Here is the simplified version of the class which is stored as value in a map which works fine in VS2008 (note that all members are private):
class Value{
friend class FriendClass;
friend class std::map<std::string, Value>;
friend struct std::pair<const std::string, Value>;
friend struct std::pair<std::string, Value>;
Value() {..}
Value(Value const& other) {..}
... rest members...
};
Code (called from FriendClass, so this can reach private constructors) :
FriendClass::func()
{
std::map<const std::string, Value> map;
map.insert(std::make_pair(std::string("x"), Value()));
}
This compiles w/o any error in VS2008, but fails on VS2015/C++11:
file.cpp(178): error C2664: 'std::_Tree_iterator>>> std::_Tree>::insert(std::_Tree_const_iterator>>>,const std::pair &)': cannot convert argument 1 from 'std::pair' to 'std::pair &&'
with
[
_Kty=std::string,
_Ty=Value,
_Pr=std::less,
_Alloc=std::allocator>
]
and
[
_Kty=std::string,
_Ty=Value
]
file.cpp(178): note: Reason: cannot convert from 'std::pair' to 'std::pair'
with
[
_Kty=std::string,
_Ty=Value
]
file.cpp(178): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
If I make the Value copy constructor public, it compiles fine in VS2015 as well. But that was private with purpose, and only made available for std::map and std::pair. However, it seems in C++11 additional friend access are also necessary to declare. Which are these?
Thank you.
I don't have access to the compilers you mentioned, but here's what I'm seeing on g++ 5.3.
Consider the following essentially-same version of your question:
#include <map>
#include <utility>
class foo
{
friend std::pair<const int, foo>;
foo(const foo &other){}
public:
foo(){}
};
int main()
{
using map_t = std::map<int, foo>;
map_t m;
m.insert(std::make_pair(2, foo()));
// m.emplace(2, foo());
}
(The default ctor is public, but that's non-essential and just makes the example shorter.)
In main, note the two lines
m.insert(std::make_pair(2, foo()));
// m.emplace(2, foo());
Reversing the comments builds fine, but the version shown doesn't:
/usr/include/c++/5/bits/stl_pair.h: In instantiation of ‘constexpr std::pair<_T1, _T2>::pair(_U1&&, const _T2&) [with _U1 = int; <template-parameter-2-2> = void; _T1 = int; _T2 = foo]’:
/usr/include/c++/5/bits/stl_pair.h:281:72: required from ‘constexpr std::pair<typename std::__decay_and_strip<_Tp>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&) [with _T1 = int; _T2 = foo; typename std::__decay_and_strip<_T2>::__type = foo; typename std::__decay_and_strip<_Tp>::__type = int]’
stuff.cpp:21:34: required from here
stuff.cpp:9:2: error: ‘foo::foo(const foo&)’ is private
foo(const foo &other){}
^
Looking at the source code std_pair.h shows that indeed it is trying to call the copy constructor. Unfortunately, you friended std::pair, but not std::make_pair.
The emplace version doesn't have this problem, but I suspect that this is implementation dependent. In general, if you want a container to store a completely opaque class, I would suggest that you use a container of std::shared_ptrs to them. This allows you to completely specify which function/class can create/copy objects in your own code, and makes no assumptions on the library's code.
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.