I thought universal reference (T&&) is supposed to take any kind of reference. But the following doesn't work.
I run into this problem when I try to be const-correct in a library that I am writing. I am new to C++ and haven't seen something like this before.
test.cpp:
enum Cv_qualifier {
constant,
non_const
};
template <Cv_qualifier Cv> class A;
template<>
class A<Cv_qualifier::constant> {
public:
template<Cv_qualifier Cv2>
void t(const A<Cv2>&& out) {}
};
template <>
class A<Cv_qualifier::non_const> {
public:
template<Cv_qualifier Cv2>
void t(const A<Cv2>&& out) {}
};
int main()
{
A<Cv_qualifier::non_const> a;
A<Cv_qualifier::constant> b;
a.t(b);
}
Error (compiled with g++ test.cpp -std=c++11):
test.cpp: In function ‘int main()’:
test.cpp:24:10: error: cannot bind ‘A<(Cv_qualifier)0u>’ lvalue to ‘const A<(Cv_qualifier)0u>&&’
a.t(b);
^
test.cpp:17:10: note: initializing argument 1 of ‘void A<(Cv_qualifier)1u>::t(const A<Cv2>&&) [with Cv_qualifier Cv2 = (Cv_qualifier)0u]’
void t(const A<Cv2>&& out) {}
^
By the way, in the actual program, the class A does not own any actual data, and contain references to another class that actually hold the data. I hope this means I am not constantly create indirection/copy data when I allow the member function t of class A to accept temporary objects.
Universal reference, or forwarding reference, only happen because of reference collapsing. It work that way:
T&& & -> T&
T& && -> T&
T&& && -> T&&
That way, when you receive T&& in a template function, the rvalue reference can collapse to other types of reference depending of the type of T. In any other cases, when the collapsing don't happen, SomeType&& will stay SomeType&& and will be an rvalue reference.
With that said, if you want your function to support forwarding, you can do that:
template <Cv_qualifier Cv> struct A;
template<>
struct A<Cv_qualifier::constant> {
template<typename T>
void t(T&& out) {}
};
template <>
struct A<Cv_qualifier::non_const> {
template<typename T>
void t(T&& out) {}
};
Indeed, now the collapsing happen. If you want to extract the Cv_qualifier value from T, you can make yourself a type trait that do that:
template<typename>
struct CvValue;
template<Cv_qualifier cv>
struct CvValue<A<cv>> {
constexpr static Cv_qualifier value = cv;
};
Then, inside your function t, you can do that:
// v----- This is a good practice to apply a constraint
template<typename T, std::void_t<decltype(CvValue<std::decay_t<T>>::value)>* = 0>
auto t(T&& out) {
constexpr auto cv = CvValue<std::decay_t<T>>::value;
// do whatever you want with cv
}
If you can't use C++17's std::void_t, you can implement it like that:
template<typename...>
using void_t = void;
However, if you only want to test if T is an A<...>, use this:
template<typename>
struct is_A : std::false_type {};
template<Cv_qualifier cv>
struct is_A<A<cv>> : std::true_type {};
Don't forget, to use it with std::decay_t:
template<typename T, std::enable_if_t<std::is_A<std::decay_t<T>>::value>* = 0>
void t(T&& out) {}
Related
I have a function which has an option parameter of type T.
template<typename T>
void foo( const T& t = T() )
{ t.stuff; }
This was all fine but I now have a scenario where T becomes void. In this case I expected a no-operation empty function. The only workable solution I have requires three separate declarations and I have many of these sort of methods:
template<typename T>
void foo( const T& t)
{ t.stuff; }
template<typename T>
inline void foo()
{ foo(T()); }
template<>
inline void foo<void>() {}
Ideally I hope there should be a more elegant solution to overload the 'Void' function without resorting to a 3rd declaration? Especially with new C++17 solving so many things these days! A more concise syntax that was shorter coudl be nice...
A simpler solution (in that there are only 2 overloads) would be something like this:
template<typename T>
void foo( const T& t = T() ) {
t.stuff;
}
template<typename T>
std::enable_if_t<std::is_void_v<T>>
foo() {}
// void foo(const T&); is the only viable overload for non-void T,
// since std::enable_if_t SFINAEs
// void foo(); is the only viable overload for void T,
// since const T& forms a reference to void
And this can be slightly shortened with alias templates since you use this pattern a lot:
template<typename T, typename TypeIfVoid = void>
using if_void = std::enable_if_t<std::is_void_v<T>, TypeIfVoid>;
template<typename T>
void foo(const T& t = T()) {
t.stuff;
}
template<typename T>
if_void<T> foo() {}
Two default template parameters will do it:
template<class T> using FallbackT = std::conditional_t<std::is_void_v<T>, int, T>;
template<class T = int&, class U = FallbackT<T>>
void foo(U const& t = U()) {
if constexpr (!std::is_void_v<T>)
t.stuff();
}
Example.
int& is the default for T so that compilation fails (default-constructing a reference at U()) if someone attempts to call foo() without either a template argument or an actual argument (try uncommenting it in the example).
I'm using int within FallbackT alias template since U just needs to be something that is default-constructible - this isn't visible to the user.
If you want to be fancy (and prevent misuse) you could add a variadic guard and use closure types:
template<
class T = decltype([]{})&,
class...,
class U = std::conditional_t<std::is_void_v<T>, decltype([]{}), T>>
void foo(U const& t = U()) {
if constexpr (!std::is_void_v<T>)
t.stuff();
}
Here, the variadic guard prevents specifying U explicitly, as e.g. foo<int, long>(); the closure types make it impossible for someone to call foo with those types by any other means - this is probably unnecessary.
Ideally I hope there should be a more elegant solution to overload the 'Void' function without resorting to a 3rd declaration? Especially with new C++17 solving so many things these days! A more concise syntax that was shorter coudl be nice...
Well... without a 3rd declaration, yes (you can use only one).
More elegant... it's question of tastes, I suppose.
More coincise syntax... well... almost the same, I suppose.
Anyway, I propose the following version, if constexpr and std::conditional_t based.
template <typename T,
typename U = std::conditional_t<std::is_same_v<T, void>,
int,
T>>
void foo (U const & u = U{})
{
if constexpr ( std::is_same_v<T, void> )
{ /* do nothing */ }
else
{ /* do u.stuff; */ }
}
I created a struct with two static functions for testing purposes. The first instance of f is called when an l-value reference is passed. The second instance is called when an r-value is passed:
template <typename _Tp>
struct T {
static constexpr void f(_Tp&) { std::cout << "f(T&) is called!\n"; }
static constexpr void f(_Tp&&) { std::cout << "f(T&&) is called!\n"; }
};
When I was experimenting with strong types, I found out the first instance, T::f(_Tp&) was called when I attempted to create the strong type implicitly. Why is this? (See following)
using T_int = T<int>;
T_int::f(
typename strong_types::create_strong_type<int, struct tag>(5)()
); // calls f::(T&) (?)
using KG = typename strong_types::create_strong_type<double, struct KG_tag>;
T_int::f(KG(4.2)()); // calls f(T&&)
Note that operator() returns the value given through the constructor.
Feel free to ask if I need to elaborate.
EDIT: strong_types is a namespace. It exists among other things of the alias create_strong_type:
namespace strong_type {
template <typename T, typename tag>
using create_strong_type = Strong_Type<T, tag>;
...
}
...
template <typename T, typename tag>
struct Strong_Type {
constexpr explicit Strong_Type(const T& value) : _value(value) {}
constexpr explicit Strong_Type(T&& value) : _value(std::move(value)) {}
constexpr T& operator()() noexcept { return _value; }
private:
T _value;
};
The difference is not due to using an alias (using), but to the type you pass as first template argument to create_strong_type. In one case, it's an int, and in the other, a double.
Try T<double>::f(KG(4.2)()); and you will see the argument is passed as lvalue reference (because of the return type of Strong_Type::operator(), which is T&).
I am writing a kind of container class, for which I would like to offer an apply method which evaluates a function on the content of the container.
template<typename T>
struct Foo
{
T val;
/** apply a free function */
template<typename U> Foo<U> apply(U(*fun)(const T&))
{
return Foo<U>(fun(val));
}
/** apply a member function */
template<typename U> Foo<U> apply(U (T::*fun)() const)
{
return Foo<U>((val.*fun)());
}
};
struct Bar{};
template class Foo<Bar>; // this compiles
//template class Foo<int>; // this produces an error
The last line yields error: creating pointer to member function of non-class type ‘const int’. Even though I only instantiated Foo and not used apply at all. So my question is: How can I effectively remove the second overload whenever T is a non-class type?
Note: I also tried having only one overload taking a std::function<U(const T&)>. This kinda works, because both function-pointers and member-function-pointers can be converted to std::function, but this approach effectively disables template deduction for U which makes user-code less readable.
Using std::invoke instead helps, it is much easier to implement and read
template<typename T>
struct Foo
{
T val;
template<typename U> auto apply(U&& fun)
{
return Foo<std::invoke_result_t<U, T>>{std::invoke(std::forward<U>(fun), val)};
}
};
struct Bar{};
template class Foo<Bar>;
template class Foo<int>;
However, this won't compile if the functions are overloaded
int f();
double f(const Bar&);
Foo<Bar>{}.apply(f); // Doesn't compile
The way around that is to use functors instead
Foo<Bar>{}.apply([](auto&& bar) -> decltype(auto) { return f(decltype(bar)(bar)); });
Which also makes it more consistent with member function calls
Foo<Bar>{}.apply([](auto&& bar) -> decltype(auto) { return decltype(bar)(bar).f(); });
In order to remove the second overload you'd need to make it a template and let SFINAE work, e. g. like this:
template<typename T>
struct Foo
{
T val;
//...
/** apply a member function */
template<typename U, typename ObjT>
Foo<U> apply(U (ObjT::*fun)() const)
{
return Foo<U>((val.*fun)());
}
};
Alternatively, you could remove the second overload altogether, and use lambda or std::bind:
#include <functional> // for std::bind
template<typename T>
struct Foo
{
T val;
/** apply a member function */
template<typename U, typename FuncT>
Foo<U> apply(FuncT&& f)
{
return {f(val)};
}
};
struct SomeType
{
int getFive() { return 5; }
};
int main()
{
Foo<SomeType> obj;
obj.apply<int>(std::bind(&SomeType::getFive, std::placeholders::_1));
obj.apply<int>([](SomeType& obj) { return obj.getFive(); });
}
How can I effectively remove the second overload whenever T is a non-class type?
If you can use at least C++11 (and if you tried std::function I suppose you can use it), you can use SFINAE with std::enable_if
template <typename U, typename V>
typename std::enable_if<std::is_class<V>{}
&& std::is_same<V, T>{}, Foo<U>>::type
apply(U (V::*fun)() const)
{ return Foo<U>((val.*fun)()); }
to impose that T is a class.
Observe that you can't check directly T, that is a template parameter of the class, but you have to pass through a V type, a template type of the specific method.
But you can also impose that T and V are the same type (&& std::is_same<V, T>{}).
When compiled, the following code causes this error:
'Container::Wrapper::Wrapper(S)': member function already
defined or declared
Does the compiler think S&& in the constructor of Wrapper is a forwarding reference?
template<typename T>
struct Container
{
template<class S>
struct Wrapper {
S obj;
Wrapper(S&& obj) : obj(std::forward<S>(obj)){}
Wrapper(const S& obj) : obj(obj){}
};
template<class S>
void push_back(S&& obj) {
void *storage = malloc(sizeof(Wrapper<S>));
new (storage) Wrapper<S>(std::forward<S>(obj));
}
};
struct Foobar{};
int main()
{
Container<Foobar> cont;
Foobar foobar;
cont.push_back(foobar);
return 0;
}
Looking at an example from here, I don't understand how what I'm doing is any different:
template <class T, class Allocator = allocator<T> >
class vector {
public:
...
void push_back(T&& x); // fully specified parameter type ⇒ no type deduction;
... // && ≡ rvalue reference
};
Edit:
The solution to this issue was to modify push_back to remove the reference from the type being used to instantiate the Wrapper:
template<class S>
void push_back(S&& obj) {
typedef std::remove_reference<S>::type U;
void *storage = malloc(sizeof(Wrapper<U>));
new (storage) Wrapper<U>(std::forward<S>(obj));
}
There are no universal references involved it the implementation of Wrapper. The only universal reference in your code is Container<>::push_back's parameter.
When you call cont.push_back(foobar);, parameter S of push_back is deduced as Foobar &.
Later you attempt to instantiate your Wrapper<S> with S == Foobar &. Reference collapsing rules dictate that in Wrapper's constructor parameter declarations S && turns into Foobar & and const S & also turns into Foobar &.
This means that you end up with two supposedly "overloaded" Wrapper::Wrapper constructors, which have identical signatures. This is the reason for the error message you observe.
If you attempt to instantiate std::vector with a template argument of lvalue reference type, you will run into exactly the same problem for its push_back overloads. However, with such an attempt compilation will typically fail miserably for other reasons, well before it gets to push_back overloads.
A more distilled example would look as follows
template <typename T> struct MyVector {
void foo(T &&) {}
void foo(const T &) {}
};
int main() {
MyVector<int &> v;
}
and will produce the same error.
The fairly non-obvious part here is that const T & with T = U & actually becomes U & and not const U &. But that's indeed the case.
#include <utility>
#include <vector>
#include <cstdint>
template <typename T>
struct Base
{
protected:
Base(T&& data):
data(std::forward(data)){
}
virtual ~Base(){};
public:
T getData() const {return data;}
void setData(T&& data) {
this->data = std::forward(data);
}
private:
T data;
};
struct DerivedA: public Base<int>
{
public:
DerivedA(int data):
Base(std::move(data)){//Should I use std::forward instead of std::move here?
}
};
struct DerivedB: public Base<const std::vector<uint16_t> >
{
public:
DerivedB(const std::vector<uint16_t>& data):
Base(std::move(data)){
}
};
My requirements is to have 0 copying of objects when creating the Derived Classes above.
But no matter how I write the above I get compiler errors, these are the latest:
bin/Base.h: In instantiation of ‘Base<T>::Base(int, int, int, T&&) [with T = int]’:
bin/Base.h:33:82: required from here
bin/Base.h:12:96: error: no matching function for call to ‘forward(int&)’
/usr/include/c++/4.7/bits/move.h:77:5: note: template<class _Tp> constexpr _Tp&& std::forward(typename std::remove_reference<_Tp>::type&)
/usr/include/c++/4.7/bits/move.h:77:5: note: template argument deduction/substitution
What am I doing wrong here?
Also, should I do std::move(data) when data in an int or std::forward?
If you want to perfectly forward an argument, you'll either need to provide the corresponding three overloads or make the argument a template argument. In either case, when you want to use std::forward() you'll need to specify the first template argument as it is not deduced. Most likely, you'd use something like this:
template <typename T>
class Base {
public:
template <typename A>
Base(A&& data): data(std::forward<A>(data)) {}
};
Trying to std::move(data) when data is a std::vector<uint16_t> const& won't move the vector nor will it make the object look like a non-const rvalue: if you want to make the vector movable, you'll need to pass it as non-const reference, an rvalue, or a value. You may also want to deduce the type or overload on std::vector<uint16_t>&& and std::vector<uint16_t>&. For both of these overloads using std::move() does the trick. If you deduce the type, you'll use std::forward<A>(data) again. In case the deduced type looks to scare, you can constrain it using std::enable_if<...> using something like this:
template <typename A>
DerivedB(A&& arg,
typename std::enable_if<std::is_same<typename std::decay<A>::value,
std::vector<uint16_t>>::value>::type* = 0):
Base(std::forward<A>(arg)) {
}