I have a function which sorts two vectors with the first of them as ordering criterion. Its signature is
template<typename A, typename B>
void sort(A&& X, B&& Y)
{
..
}
The problem is that universal references would allow nonsense cases like
sort(vector<int>{ 2,1,3 }, vector<int>{ 3,1,2 });
where an rvalue will be destroyed afterwards (nonsense).
Asking explicitly for a lvalue doesn't work since
template<typename A, typename B>
void sort(A& X, B& Y) ... // (*)
sort(vector<int>{2,1,3}, vector<int>{3,1,2});
for some reason the above compiles (I thought only const lvalues were allowed to bind to rvalues and to prolong their lifetime?).
If I add const to the lvalue reference then the function will no longer be able to modify the vectors and sort them.
My questions are:
1) Why in the example marked with // (*) can I bind a rvalue to a lvalue that is not even const ? Why instead something like int& r = 20; isn't allowed? What's the difference?
2) How can I solve my issue i.e. having the function accept only lvalues and not rvalue temporaries? (If it's possible, of course)
Obviously I'm allowed to use any C++ version available
The answer is: your compiler is wrong.
Check on gcc or clang or similar and you'll get something like this:
prog.cpp: In function 'int main()': prog.cpp:9:45: error: invalid
initialization of non-const reference of type 'std::vector&' from
an rvalue of type 'std::vector' sort(vector{2,1,3},
vector{3,1,2});
^ prog.cpp:6:6: note: initializing argument 1 of 'void sort(A&, B&) [with A =
std::vector; B = std::vector]' void sort(A& X, B& Y) { }
You can use the /Za compiler option to turn this into an error:
error C2664: 'void sort<std::vector<int,std::allocator<_Ty>>,std::vector<_Ty,std::allocator<_Ty>>>(A &,B &)' : cannot convert argument 1
from 'std::vector<int,std::allocator<_Ty>>' to 'std::vector<int,std::allocator<_Ty>> &'
with
[
_Ty=int
, A=std::vector<int,std::allocator<int>>
, B=std::vector<int,std::allocator<int>>
]
and
[
_Ty=int
]
and
[
_Ty=int
]
A non-const reference may only be bound to an lvalue
Note that /Za has had quite some issues in the past and even nowadays still breaks <windows.h>, so you cannot use it for all compilation units anyway. In a 2012 posting titled "MSVC /Za considered harmful", Microsoft senior engineer Stephan T. Lavavej even recommends not using the flag, but you should also have a look at the comments at STL Fixes In VS 2015, Part 2, where he says:
We've definitely had meetings about the /Za and /Zc conformance
options. We ultimately want to get to a point where VC is conformant
by default, without having to request extra options, so that becomes
the most-used and most-tested path. As you can see in the post, I've
been working towards this in the STL by removing non-Standard
machinery whenever possible.
So, chances are this will be a compilation error by default in some future version of MSVC.
One other thing: The C++ standard does not distinguish between errors and warnings, it only talks about "diagnostic messages". Which means that MSVC actually is conforming as soon it produces a warning.
As noted by other answers, the compiler is wrong.
Without having to change compiler of compiler options:
struct sfinae_helper {};
template<bool b>
using sfinae = typename std::enable_if<b, sfinae_helper>::type*;
// sfinae_helper, because standard is dumb: void*s are illegal here
template<class A, class B,
sfinae<!std::is_const<A>::value&&!std::is_const<B>::value> = nullptr
>
void sort(A& X, B& Y) ... // (*)
sort(vector<int>{2,1,3}, vector<int>{3,1,2});
will fail to compile in MSVC2013 as well, and should be compliant in compliant compilers.
Note that while deducing A and B as const X is not legal under the standard, explicitly passing const X as A or B is.
A final approach is:
template<typename A, typename B>
void sort(A& X, B& Y) ... // (*)
template<typename A, typename B>
void sort(A&& X, B&& Y) = delete;
where we generate an explicitly deleted one that should be preferred to the A&, B& one. I do not know if MSVC properly picks the perfectly forwarded one in that case, but I hope so.
As an answer to the X problem you're trying to solve rather than the Y problem you asked... the right answer is that you shouldn't do what you're trying to do. Being unable to imagine how something can be useful is not an adequate reason to go out of your way to prevent people from being able to do it.
And, in fact, I don't even have to suggest this in the abstract: here are two concrete examples where accepting a temporary object would be useful.
You might only care about one of the two objects:
interesting_container A;
// fill A
sort(an_ordering_criterion(), A);
The containers aren't 'self-contained'; e.g. a container that provides a view into another one:
vector<int> A, B;
// fill A and B
sort(make_subsequence(A, 1, 10), make_subsequence(B, 5, 14));
You can explicitly delete undesired overloadings of sort function:
#include <iostream>
#include <vector>
#include <cstdlib>
template< typename X, typename Y >
void
sort(X &, Y &)
{
static_assert(!std::is_const< X >{});
static_assert(!std::is_const< Y >{});
}
template< typename X, typename Y >
int
sort(X const &, Y &) = delete;
template< typename X, typename Y >
int
sort(X &, Y const &) = delete;
template< typename X, typename Y >
int
sort(X const &, Y const &) = delete;
int
main()
{
std::vector< int > v{1, 3, 5};
std::vector< int > const c{2, 4, 6};
::sort(v, v); // valid
{ // has been explicitly deleted
//::sort(v, c);
//::sort(c, v);
//::sort(c, c);
}
{ // not viable: expects an l-value for 1st argument
//::sort(std::move(v), v);
//::sort(std::move(v), c);
//::sort(std::move(c), v);
//::sort(std::move(c), c);
}
{ // not viable: expects an l-value for 2nd argument
//::sort(v, std::move(v));
//::sort(v, std::move(c));
//::sort(c, std::move(v));
//::sort(c, std::move(c));
}
{ // not viable: expects an l-value for 1st or 2nd argument
//::sort(std::move(v), std::move(v));
//::sort(std::move(v), std::move(c));
//::sort(std::move(c), std::move(v));
//::sort(std::move(c), std::move(c));
}
return EXIT_SUCCESS;
}
Related
The program below generates a compiler error:
MSVC: error C2782: 'double dot(const V &,const V &)': template parameter 'V' is ambiguous
GCC: deduced conflicting types for parameter 'const V' ('Matrix<3, 1>' and 'UnitVector')
I had thought that it would not have this problem because the constructor UnitVector(Vector) is marked explicit, and therefore the arguments (of the call to dot()) can only resolve as Vector with implicit conversions. Can you tell me what I am misunderstanding? Does the compiler consider explicit constructors as implicit conversions when resolving template parameters?
template<int M, int N>
struct Matrix {
};
using Vector = Matrix<3,1>;
struct UnitVector : Vector{
UnitVector(){}
explicit UnitVector(const Vector& v)
{}
operator const Vector&(){
return *static_cast<const Vector*>(this);
}
};
template<typename V>
double dot(const V& a, const V& b){
return 0.0;
}
int main()
{
dot(Vector(),UnitVector());
}
No, that does not work. But actually, you don't need the template
double dot(const Vector &, const Vector &) {...}
works. You don't even need the conversion operator defined in UnitVector. Child to base conversions are done implicitly.
If you want to generally take two types that are implicitly convertible to a common type, the following should work (untested)
template<class U>
double dot_impl(const U&, const U&) {...}
template<class U, class V>
auto dot(const U &u, const V &v) {
return dot_impl<std::common_type_t<U, V>>(u, v);
}
Since the template parameter is explicit, the implicit conversions to thw common type of those two are done in the call, so everything works nice. I moved the original dot to dot_impl, since otherwise we would call dot with one template parameter, which could still be ambiguous.
Is it valid to have variations of the same template function that differ by the type of a non-type member?
template<typename T, unsigned int V>
void f(unsigned int& v) { v = V; }
template<typename T, bool B>
void f(bool& b) { b = B; }
The intent is that one be able to call
unsigned int meaningOfLife;
f<sometype, 42>(meaningOfLife);
bool areYouAlive;
f<sometype, true>(areYouAlive);
clang and gcc are silent but MSVC reports
warning C4305: 'specialization': truncation from 'int' to 'bool'
I'd like to avoid requiring specification of the constant type:
f<sometype, bool, true>
and want to ensure that the constant value and the destination value match.
---- mcve ----
#include <iostream>
template<unsigned int V>
void f(unsigned int& v) { v = V; }
template<bool B>
void f(bool& b) { b = B; }
int main()
{
unsigned int u { 0 };
bool b { false };
f<42>(u);
f<true>(b);
std::cout << u << b;
}
Rextester example: http://rextester.com/VIGNP16100
Warning(s):
source_file.cpp(14): warning C4305: 'specialization': truncation from 'int' to 'bool'
/LIBPATH:C:\boost_1_60_0\stage\lib
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23506 for x64
421
Short answer: The code is OK and MSVC emits a bogus warning.
MSVC and g++ both have bugs in non-type template argument matching but they do select the right one for your particular example.
Long answer: It is OK to have overloaded function templates with non-type template parameters.
However the matching of a template-argument to a template declaration does not work as might be expected (by me anyway). ALL matching templates are entered into overload resolution. It does not, at any stage, prefer an "exact match".
According to C++17 [temp.arg.nontype/]2, converted constant expressions are allowed. That means, for example:
42 matches int and unsigned int.
42u matches int and unsigned int.
1u matches unsigned int, int and bool.
Note that a converted constant expression cannot contain a narrowing conversion, and int to bool is narrowing unless the value is a constant expression of value 0 or 1. So 42 does not match bool. (Ref: C++17 [expr.const]/4).
If we had the following setup:
template<unsigned int V> void g() {}
template<bool B> void g() {}
then the correct behaviour is:
g<42>() calls g<unsigned int>.
g<1>() is ambiguous.
g<1u>() is ambiguous.
MSVC 2017 and g++ 7,8 all incorrectly allow g<42> to match g<bool>, and report g<42> as ambiguous.
MSVC emits the warning that you see whilst generating the invalid match; g++ gives no diagnostic at all. If we remove the unsigned int overload then g++ silently accepts the invalid code with no diagnostic.
In your code there is a non-const lvalue reference parameter:
template<unsigned int V> void h(unsigned int&) {}
template<bool B> void h(bool&) {}
This makes a difference because overload resolution can make a selection based on the function argument. For the call:
unsigned int m;
h<1u>(m);
then both overloads of h are entered into overload resolution, however then h<unsigned int> wins because h<bool>(m) would be invalid.
As discussed above, The call h<42>(m); wins at the first stage because this cannot match h<bool>; but in MSVC++ (and g++) it incorrectly allows h<bool> to go through at this stage, but prunes it later as for the h<1u> case.
I noticed that libstdc++'s implementation of std::ignore takes a const T& argument, which can't bind to a volatile rvalue. Hence the following code fails to compile:
#include <tuple>
#include <utility>
struct C {};
using VC = C volatile;
int main() {
std::tuple<VC> t;
std::tie(std::ignore) = std::move(t);
}
(http://coliru.stacked-crooked.com/a/7bfc499c1748e59e)
Is this in violation of the standard, or is there a clause that renders this undefined behaviour?
I'm not a language lawyer, so I'm going to be answering this question as directly as possible.
ignore is found in the synopsis of tuple in tuple.general as such:
// [tuple.creation], tuple creation functions:
const unspecified ignore;
As you noticed, the libstdc++ implementation defines ignore like this:
// A class (and instance) which can be used in 'tie' when an element
// of a tuple is not required
struct _Swallow_assign
{
template<class _Tp>
const _Swallow_assign&
operator=(const _Tp&) const
{ return *this; }
};
Whereas the libc++ version defines it like this:
template <class _Up>
struct __ignore_t
{
template <class _Tp>
_LIBCPP_INLINE_VISIBILITY
const __ignore_t& operator=(_Tp&&) const {return *this;}
};
As such, it compiles in libc++. Now the definition of std::tie can be found in [tuple.creation] which says:
Returns: tuple<Types&...>(t...). When an argument in t is
ignore, assigning any value to the corresponding tuple element has
no effect.
This doesn't say anything about ignore itself, so I'm going to chalk this up to unspecified behavior. You can argue it's undefined behavior by omission, but that might be stretching it.
Comment:
// g++ 4.8.4
int main() {
volatile int vi;
std::ignore = vi;
// error: no match for ‘operator=’ (
// operand types are ‘const std::_Swallow_assign’
// and ‘std::remove_reference<volatile int&>::type {aka volatile int}’
// )
// std::ignore = std::move(vi);
// However this compiles:
volatile int&& vir = std::move(vi);
std::ignore = vir;
}
I am trying to write a function that accepts only lvalue Eigen expressions passed via const references. My first idea was to keep only the overload const Eigen::MatrixBase<Derived>& and delete the Eigen::MatrixBase<Derived>&& one. To my surprise, the deleted function was not part of the overload candidate set. So I tried the code below
#include <iostream>
#include <Eigen/Dense>
#define PRINT_MY_NAME std::cout << __PRETTY_FUNCTION__ << '\n'
template<typename Derived>
void f(const Eigen::MatrixBase<Derived>&) // (1)
{
PRINT_MY_NAME;
}
template<typename Derived>
void f(Eigen::MatrixBase<Derived>&&) // (2)
{
PRINT_MY_NAME;
}
int main()
{
Eigen::MatrixXd A;
f(A); // invokes (1)
f(A + A); // invokes also (1) !!!
}
which outputs (gcc5.2)
void f(const Eigen::MatrixBase&) [with Derived = Eigen::Matrix < double, -1, -1>]
void f(const Eigen::MatrixBase&) [with Derived = Eigen::CwiseBinaryOp < Eigen::internal::scalar_sum_op < double>, const Eigen::Matrix < double, -1, -1>, const Eigen::Matrix < double, -1, -1> >]
so clearly the rvalue overload is not considered. It is now clear for me that the second one is not a better match, since I pass a rvalue Eigen expression, which is convertible to Eigen::MatrixBase<>, but is not the exact same type. Now comes my question:
How can I disable or detect rvalue Eigen expressions passed as parameters for f? The issue is that an expression can have an arbitrary type (Eigen uses expression templates), like CwiseBinaryOp<...CwiseBinaryOp<...>> and so on. This is part of a larger problem in which I have a utility make-like function which takes a lvalue and binds it to a const reference expression in a class. If the expression is a rvalue, then all bets are off, since reference binding is not propagating via a constructor argument, so I want to forbid passing rvalue Eigen expressions.
I think I found what was going on: the result of the expression template A + A was a const, so there was a CV-mismatch. Adding const to the second overload does it:
template<typename Derived>
void f(const Eigen::MatrixBase<Derived>&&) // (2)
{
PRINT_MY_NAME;
}
Given GMan's deliciously evil auto_cast utility function concocted here, I've been trying to figure out why it doesn't compile for me when I'm trying to auto_cast from an rvalue (on MSVC 10.0).
Here's the code that I'm using:
template <typename T>
class auto_cast_wrapper : boost::noncopyable
{
public:
template <typename R>
friend auto_cast_wrapper<R> auto_cast(R&& pX);
template <typename U>
operator U() const
{
return static_cast<U>( std::forward<T>(mX) );
}
private:
//error C2440: 'initializing': cannot convert from 'float' to 'float &&'
auto_cast_wrapper(T&& pX) : mX(pX) { }
T&& mX;
};
template <typename R>
auto_cast_wrapper<R> auto_cast(R&& pX)
{
return auto_cast_wrapper<R>( std::forward<R>(pX) );
}
int main()
{
int c = auto_cast( 5.0f ); // from an rvalue
}
To the best of my ability I've tried to follow the C++0x reference collapsing rules and the template argument deduction rules outlined here, and as far as I can tell the code given above should work.
Recall that in pre-0x C++, it is not allowed to take a reference to a reference: something like A& & causes a compile error. C++0x, by contrast, introduces the following reference collapsing rules:
A& & becomes A&
A& && becomes A&
A&& & becomes A&
A&& && becomes A&&
The second rule is a special template argument deduction rule for function templates that take an argument by rvalue reference to a template argument:
template<typename T>
void foo(T&&);
Here, the following rules apply:
When foo is called on an lvalue of type A, then T resolves to A& and hence, by the reference collapsing rules above, the argument type effectively becomes A&.
When foo is called on an rvalue of type A, then T resolves to A, and hence the argument type becomes A&&.
Now when I mouse over the call to auto_cast( 5.0f ), the tooltip correctly displays its return value as auto_cast_wrapper<float>. This meaning that the compiler has correctly followed rule 2:
When foo is called on an rvalue of type A, then T resolves to A.
So since we have an auto_cast_wrapper<float>, the constructor should instantiate to take a float&&. But the error message seems to imply that it instantiates to take a float by value.
Here's the full error message, showing again that T=float correctly yet the T&& parameter becomes T?
main.cpp(17): error C2440: 'initializing' : cannot convert from 'float' to 'float &&'
You cannot bind an lvalue to an rvalue reference
main.cpp(17) : while compiling class template member function 'auto_cast_wrapper<T>::auto_cast_wrapper(T &&)'
with
[
T=float
]
main.cpp(33) : see reference to class template instantiation 'auto_cast_wrapper<T>' being compiled
with
[
T=float
]
Any thoughts?
You forgot to std::forward the T&& argument to the auto_cast_wrapper constructor. This breaks the forwarding chain. The compiler now gives a warning but it seems to work fine.
template <typename T>
class auto_cast_wrapper
{
public:
template <typename R>
friend auto_cast_wrapper<R> auto_cast(R&& pX);
template <typename U>
operator U() const
{
return static_cast<U>( std::forward<T>(mX) );
}
private:
//error C2440: 'initializing': cannot convert from 'float' to 'float &&'
auto_cast_wrapper(T&& pX) : mX(std::forward<T>(pX)) { }
auto_cast_wrapper(const auto_cast_wrapper&);
auto_cast_wrapper& operator=(const auto_cast_wrapper&);
T&& mX;
};
template <typename R>
auto_cast_wrapper<R> auto_cast(R&& pX)
{
return auto_cast_wrapper<R>( std::forward<R>(pX) );
}
float func() {
return 5.0f;
}
int main()
{
int c = auto_cast( func() ); // from an rvalue
int cvar = auto_cast( 5.0f );
std::cout << c << "\n" << cvar << "\n";
std::cin.get();
}
Prints a pair of fives.
Sorry for posting untested code. :)
DeadMG is correct that the argument should be forwarded as well. I believe the warning is false and the MSVC has a bug. Consider from the call:
auto_cast(T()); // where T is some type
T() will live to the end of the full expression, which means the auto_cast function, the auto_cast_wrapper's constructor, and the user-defined conversion are all referencing a still valid object.
(Since the wrapper can't do anything but convert or destruct, it cannot outlive the value that was passed into auto_cast.)
I fix might be to make the member just a T. You'll be making a copy/move instead of casting the original object directly, though. But maybe with compiler optimization it goes away.
And no, the forwarding is not superfluous. It maintains the value category of what we're automatically converting:
struct foo
{
foo(int&) { /* lvalue */ }
foo(int&&) { /* rvalue */ }
};
int x = 5;
foo f = auto_cast(x); // lvalue
foo g = auto_cast(7); // rvalue
And if I'm not mistaken the conversion operator shouldn't be (certainly doesn't need to be) marked const.
The reason it doesn't compile is the same reason for why this doesn't compile:
float rvalue() { return 5.0f }
float&& a = rvalue();
float&& b = a; // error C2440: 'initializing' : cannot convert from 'float' to 'float &&'
As a is itself an lvalue it cannot be bound to b. In the auto_cast_wrapper constructor we should have used std::forward<T> on the argument again to fix this. Note that we can just use std::move(a) in the specific example above, but this wouldn't cover generic code that should work with lvalues too. So the auto_cast_wrapper constructor now becomes:
template <typename T>
class auto_cast_wrapper : boost::noncopyable
{
public:
...
private:
auto_cast_wrapper(T&& pX) : mX( std::forward<T>(pX) ) { }
T&& mX;
};
Unfortunately it seems that this now exhibits undefined behaviour. I get the following warning:
warning C4413: 'auto_cast_wrapper::mX' : reference member is initialized to a temporary that doesn't persist after the constructor exits
It appears that the literal goes out of scope before the conversion operator can be fired. Though this might be just a compiler bug with MSVC 10.0. From GMan's answer, the lifetime of a temporary should live until the end of the full expression.