I was watching Dr. Walter E. Brown's Template Meta-programming talk. In his presentation he presents code like so for is_copy_assignable:
template<class U, class = decltype(declval<U&>() = declval<U const&>())>
static true_type try_assignment(U &&);
What I am having trouble with is how is the assignment operator being called in this instance. When I try and reason about this code and say substitute a dummy type, say int, in place of the U I get:
template<class U, class = decltype(declval<int&>() = declval<int const&>())>
template<class U, class = decltype(int& = int const&)>
So I am wondering then how can this help us determine if the assignment operator is valid. If I understand declval correctly this code will not even evaluate so then how can one determine from int& = int const&, which doesn't even evaluate, if there is or isn't a assignment operator defined for U.
I understand that in most cases the copy-assignment operator would be defined as
C& operator=(const C& other)
which looks a lot like what is above, but still since nothing is being evaluated then what use is this information.
I don't really follow what you intended in performing the following step:
template<class U, class = decltype(declval<int&>() = declval<int const&>())>
template<class U, class = decltype(int& = int const&)>
declval says: pretend to return a value of type of the template argument. I say pretend because the function isn't really defined, only declared, so it can only be used in an unevaluated context. Inside of decltype is an unevaluated contexts because you're only checking types.
So, the expression decltype(declval<int&>() = declval<int const&>()) is basically saying: if I had an int&, call it x, and I had a int const&, call it y, what is the type of the expression x = y?
As you can probably guess this calls the assignment operator and this expression will have a valid type (in this case, int&). If you change int to unique_ptr, this expression won't have a valid type, because unique_ptr is not assignable, causing type substitution to fail and eliminating this template from the overload or specialization set.
The reason to have declval at all is because it lets you create a value of any type; this is particularly useful for a) creating something of reference type, and b) creating non-reference types without assuming that they have e.g. a default constructor. Thus, use of declval is extremely ubiquitous in high quality TMP.
Related
To make a concept checking if a type can be converted without narrowing to another, it is proposed here to make it using std::forward and std::type_identity_t like this:
template<class T, class U>
concept __construct_without_narrowing = requires (U&& x) {
{ std::type_identity_t<T[]>{std::forward<U>(x)} } -> T[1];
};
I understand from it why something like this:
To{std::declval<From>()}
gives incorrect results, but when i try to simplify it using another idea in the paper, writing just
template <typename From, typename To>
concept WithoutNarrowing =
requires (From x) {
{(To[1]){x}}
->std::same_as<To[1]>;
};
It seems to give the same results. What circumstances have to occur for it to give different result? Or is it equivalent? For what reason is std::forward used here?
This is the usual approach for type traits like this that involve some kind of function/constructor argument.
U is the type from which T is supposed to be constructed, but if we want to discuss the construction we also need to consider the value category of the argument. It may be an lvalue or a rvalue and this can affect e.g. which constructor is usable.
The idea is that we map the rvalue argument case to a non-reference U or rvalue reference U and the lvalue argument case to a lvalue reference U, matching the mapping of expressions in decltype and of return types with value categories in function call expressions.
Then, by the reference collapsing rules, U&& will be a lvalue reference if the constructor argument is a lvalue and otherwise a rvalue reference. Then using std::forward means that the actual argument we give to the construction will indeed be a lvalue argument when U was meant to represent one and a rvalue argument otherwise.
Your approach using {(To[1]){x}} doesn't use the forwarding and so would always only test whether construction from a lvalue can be done without narrowing, which is not what is expected if e.g. U is a non-reference.
Your approach is further incorrect because (To[1]){x} is not valid syntax in standard C++. If X is a type you can have X{x} or (X)x, but not (X){x}. The last syntax is part of C however and called a compound literal there. For that reason a C++ compiler may support it as an extension to C++. That's why the original implementation uses the round-about way with std::type_identity_t.
The implementation seems to also be written for an earlier draft of C++20 concepts. It is now not possible to use types to the right of -> directly for a requirement. Instead a concept, i.e. -> std::same_as<T[1]>, must be used as in your suggested implementation.
well there is difference between (U u), (U& u) and (U&& u) that std::forward is supposed to preserve. in case of (U u) the type has to have defined a copy constructor (since (U u) basically means "pass a copy of")
Is there any reason that c++ compiler gives error when using two different numeric variable types in std::max() function? (e.g. int and long).
I mean something like: "Sometimes we have this problem when using std::max() function for two different numeric variable types, so the compiler gives error to prevent this problem".
The compiler produces an error because it cannot perform type deduction for the template argument of std::max. This is how std::max template is declared: the same type (template parameter) is used for both arguments. If the arguments have different types, the deduction becomes ambiguous.
If you work around the deduction ambiguity by supplying the template argument explicitly, you will be able to use different types as std::max arguments
std::max(1, 2.0); // Error
std::max<double>(1, 2.0); // OK
The reason why std::max insists on using a common type for its arguments (instead of using two independent types) is described in #bolov's answer: the function actually wants to return a reference to the maximum value.
std::max returns a reference to the argument that has the maximum value. The main reason it is this way is because it is a generic function and as such it can be used with types expensive to copy. Also you might actually need just a reference to the object, instead a copy of it.
And because it returns a reference to a argument, all arguments must be of the same type.
The direct answer to the question is that it's because std::min and std::max take only one template parameter that defines the types of both arguments. If/when you try to pass arguments of different types, the compiler can't decide which of those two types to use for the template argument, so the code is ambiguous. As originally defined in C++98, std::min and std::max had signatures like these (C++03, §[lib.alg.min.max]):
template<class T> const T& min(const T& a, const T& b);
template<class T, class Compare>
const T& min(const T& a, const T& b, Compare comp);
template<class T> const T& max(const T& a, const T& b);
template<class T, class Compare>
const T& max(const T& a, const T& b, Compare comp);
So the basic idea here is that the function receives two objects by reference, and returns a reference to one of those objects. If it received objects of two different types, it wouldn't be able to return a reference to an input object because one of the objects would necessarily be of a different type than it was returning (so #bolov is correct about that part, but I don't think it's really the whole story).
With a modern compiler/standard library, if you don't might dealing with values instead of references, you could pretty easily write code on this general order:
template <class T, class U>
std::common_type<T, U> min(T const &a, U const &b) {
return b < a ? b : a;
}
template <class T, class U>
std::common_type<T, U> max(T const &a, U const &b) {
return a < b ? b : a;
}
That makes it pretty easy to deal with your case of passing an int and a long (or other pairs of types, as long as std::common_type can deduce some common type for them, and a<b is defined for objects of the two types.
But, in 1998, even if std::common_type had been available so it was easy to do, that solution probably wouldn't have been accepted (and as we'll see, it's still open to some question whether it's a great idea)--at the time, many people still thought in terms of lots of inheritance, so it was (more or less) taken for granted that you'd frequently use it in situations where both arguments were really of some derived type, something on this general order:
class Base {
// ...
virtual bool operator<(Base const &other);
};
class Derived1 : public Base {
// ...
};
class Derived2 : public Base {
// ...
};
Derived1 d1;
Derived2 d2;
Base &b = std::max(d1, d2);
In this case, the version above that returns a value instead of returning a reference would cause a serious problem. common_type<Derived1, Derived2> is going to be Base, so we'd end up slicing the argument to create an object of type Base, and returning that. This would rarely provide desirable behavior (and in some cases, such as if Base were an abstract base class, it wouldn't even compile).
There's one other point that's probably worth noting: even when applied in a seemingly simple situation, std::common_type can produce results you might not expect. For example, let's consider calling the template defined above like:
auto x = min(-1, 1u);
That leaves us with an obvious question: what type will x be?
Even though we've passed it an int and an unsigned, the type of the result is (at least potentially) neither int or unsigned (e.g., quite possibly long long)!
Consider the following:
template<typename T>
struct C {};
template<typename T, typename U>
void operator +(C<T>&, U);
struct D: C<D> {};
struct E {};
template<typename T>
void operator +(C<T>&, E);
void F() { D d; E e; d + e; }
This code compiles fine on both GCC-7 and Clang-5. The selected overload for operator + is that of struct E.
Now, if the following change takes place:
/* Put `operator +` inside the class. */
template<typename T>
struct C {
template<typename U>
void operator +(U);
};
that is, if operator + is defined inside the class template, instead of outside, then Clang yields ambiguity between both operator +s present in the code. GCC still compiles fine.
Why does this happen? Is this a bug in either GCC or Clang?
This is a bug in gcc; specifically, https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53499 .
The problem is that gcc is regarding the implicit object parameter of a class template member function as having a dependent type; that is, during function template partial ordering gcc transforms
C<D>::template<class U> void operator+(U); // #1
into
template<class T, class U> void operator+(C<T>&, U); // #1a (gcc, wrong)
when it should be transformed into
template<class U> void operator+(C<D>&, U); // #1b (clang, correct)
We can see that when compared to your
template<class T> void operator+(C<T>&, E); // #2
#2 is better than the erroneous #1a, but is ambiguous with #1b.
Observe that gcc incorrectly accepts even when C<D> is not a template at all - i.e., when C<D> is a class template full specialization:
template<class> struct C;
struct D;
template<> struct C<D> {
// ...
This is covered by [temp.func.order]/3, with clarification in the example. Note that again, gcc miscompiles that example, incorrectly rejecting it but for the same reason.
Edit: The original version of this answer said that GCC was correct. I now believe that Clang is correct according to the wording of the standard, but I can see how GCC's interpretation could also be correct.
Let's look at your first example, where the two declarations are:
template<typename T, typename U>
void operator +(C<T>&, U);
template<typename T>
void operator +(C<T>&, E);
Both are viable, but it is obvious that the second template is more specialized than the first. So GCC and Clang both resolve the call to the second template. But let's walk through [temp.func.order] to see why, in the wording of the standard, the second template is more specialized.
The partial ordering rules tell us to replace each type template parameter with a unique synthesized type and then perform deduction against the other template. Under this scheme, the first overload type becomes
void(C<X1>&, X2)
and deduction against the second template fails since the latter only accepts E. The second overload type becomes
void(C<X3>&, E)
and deduction against the first template succeeds (with T = X3 and U = E). Since the deduction succeeded in only one direction, the template that accepted the other's transformed type (the first one) is considered less specialized, and thus, the second overload is chosen as the more specialized one.
When the second overload is moved into class C, both overloads are still found and the overload resolution process should apply in exactly the same way. First, the argument list is constructed for both overloads, and since the first overload is a non-static class member, an implied object parameter is inserted. According to [over.match.funcs], the type of that implied object parameter should be "lvalue reference to C<T>" since the function does not have a ref-qualifier. So the two argument lists are both (C<D>&, E). Since this fails to effect a choice between the two overloads, the partial ordering test kicks in again.
The partial ordering test, described in [temp.func.order], also inserts an implied object parameter:
If only one of the function templates M is a non-static member of
some class A, M is considered to have a new first parameter inserted in its function parameter list. Given cv
as the cv-qualifiers of M (if any), the new parameter is of type “rvalue reference to cv A” if the optional
ref-qualifier of M is && or if M has no ref-qualifier and the first parameter of the other template has rvalue
reference type. Otherwise, the new parameter is of type “lvalue reference to cv A”. [ Note: This allows a
non-static member to be ordered with respect to a non-member function and for the results to be equivalent
to the ordering of two equivalent non-members. — end note ]
This is the step where, presumably, GCC and Clang take different interpretations of the standard.
My take: The member operator+ has already been found in the class C<D>. The template parameter T for the class C is not being deduced; it is known because the name lookup process entered the concrete base class C<D> of D. The actual operator+ that is submitted to partial ordering therefore does not have a free T parameter; it is not void operator+(C<T>&, U), but rather, void operator+(C<D>&, U).
Thus, for the member overload, the transformed function type should not be void(C<X1>&, X2), but rather void(C<D>&, X2). For the non-member overload, the transformed function type is still void(C<X3>&, E) as before. But now we see that void(C<D>&, X2) is not a match for the non-member template void(C<T>&, E) nor is void(C<X3>&, E) a match for the member template void(C<D>&, U). So partial ordering fails, and overload resolution returns an ambiguous result.
GCC's decision to continue to select the non-member overload makes sense if you assume that it is constructing the transformed function type for the member lexically, making it still void(C<X1>&, X2), while Clang substitutes D into the template, leaving only U as a free parameter, before beginning the partial ordering test.
template <class T, class U> decltype(*(T*)(0) * *(U*)(0)) mul(T x, U y) {
return x * y;
}
This piece of code was taken from Stroustrup's C++11 FAQ. I understand what it does, which is multiply two objects of varying types. What perplexes me is the syntax between the template parameters and the function definition. What is happening inside decltype? I take it that it's dereferencing an unnammed T pointer initialized to 0, and multiplying it by an unnamed U pointer being dereferenced and initialized in the same way. Am I right?
Well, if this is what is happening, then isn't the use of pointers, dereferences, and extra parenthesis superfluous? Couldn't I initialize the types like this while maintaining the desired effect?:
template <class T, class U> decltype(T(0) * U(0)) mul(T x, U y) {
return x * y;
}
This looks much cleaner to me, and it does have the same effect when multiplying two numbers like in the first...
mul(4, 3); // 12
So why does Stroustrup insist on using complex pointer, dereference and initialization syntax? This is, of course, before he introduced the new auto syntax. But anyway, my question is: Is there any difference between the two above forms of type initialization? Where he uses pointers and instantly dereferences them instead of simply doing what I did, which was to initialize the types with no pointers or dereferencing? Any response is appreciated.
Your version is not equivalent.
Your version supposes that both T and U can be constructed from 0. It's obviously wrong to expect this from matrices, yet they can be multiplied.
T(0) yields a temporary (that may bind to T&&) whilst *(T*(0)) yields a reference to an existing object (that is, T&), therefore a different operator might be selected.
However, neither Stroustrup's version nor yours end up being used in practice. At an equivalent level of compiler implementation, one would use:
template <typename T, typename U>
decltype(std::declval<T>() * std::declval<U>()) mul(T x, U y);
But it's failing to take advantage of Late Return Type specification, built to allow postponing the declaration of the return type after the function's arguments declaration: auto f(int, int) -> int. Once the arguments have been declared, they can be used, which is incredibly useful for decltype!
template <typename T, typename U>
auto mul(T x, U y) -> decltype(x * y);
This latter form is guaranteed to pick the same operator overload than the function body, at the cost of repetition (unfortunately).
Your version of the code supposes that T and U have default constructors. Stroustrup version doesn't, it creates a dummy object by dereferencing a null pointer. That doesn't matter of course because that code is not meant to be executed, it's only meant to be parsed to know the resulting type.
decltype content is unevaluated context; it doesn't matter what is there, as long as it results with a type. Think for a second about T being defined like this:
struct T
{
int operator*(const U &) { return 2; }
};
It doesn't have constructor taking int or any type that int is convertible to; therefore, T(0) does not result in an object, even in unevaluated context of decltype. Therefore, using unevaluated null references is possibly the easiest way to get proper type.
Bottom line: you don't know what constructors do T and U have, so you should use null reference that references dummy object of proper type.
I have some questions concerning function templates.
My plan was to build a wrapper which derives from a user-defined class and
not only exports the public functions of that class but also its constructors.
So I decided I would use multiple constructor templates (which I presume work exactly
the same as function templates) with 1 to n parameters to satisfy most constructors needs.
These would than simply call the constructor and do something else afterwards, like
this:
template <class T>
class Wrapper : public T
{
public:
template <class U>
Wrapper(U &u) : T(u) { doSomething(); }
template <class U, class V>
Wrapper(U &u, V &v) : T(u,v) { doSomething(); }
...
};
My intent is to register the instance within the Wrapper-Ctor somewhere else and,
from that point on, it can receive calls to virtual functions defined in T.
I had to use the reference operator in the code above, in order to guarantee that
my Wrapper-Ctor does not have any side-effects on the parameters that were passed
(copy-construction).
To my surprise this always worked, except for temporaries, which is the reason why
I am confused about the types that are inferred by the compiler in this situation.
To simplify the situation I tried to do something similiar via a template function:
template <class T>
void foo(T &t)
{
int x = ""; // intentional error
}
Calling the function like this:
std::string a;
std::string &b = a;
foo(b);
To my surprise the compiler denotes [T = std::string] in its error message.
I would have expected for this to be [T = std::string&], which would have caused
passing a reference-to-reference, which is invalid.
So, why does the compiler deduce a value-type in this situation?
Is it even possible to create a Wrapper-Ctor that does what I want, does not
have any side-effects on the parameters and also accepts temporaries?
Thanks alot!
It looks like the C++ spec explicitly states that this is the intended behavior. Specifically, if you have a template function that takes in a parameter P that depends on a template type argument, if P is a reference, then the underlying type of the reference, rather than the reference type, is used to determine what type should be used for P (see §14.8.2.1/2). Moreover, this same section says that const and volatile qualifiers are ignored during this step, so the constness can be inferred automatically.
It is not possible in C++03 to provide such a thing without manually overloading for every combination of const and non-const parameters.
No expression ever has reference type. Therefor, when argument deduction deduces against the argument expression type, it cannot make a distinction between a and b because the arguments a and b both have the same type.
Refer to [expr]p5 in the spec
If an expression initially has the type "reference to T" (8.3.2, 8.5.3), the type is adjusted to T prior to any further analysis.
Somewhat late, but since I don't think this was answered completely...
For template parameter deduction, see the previous answers.
For your problem with temporaries, make the parameters const references (as in Wrapper(const U&)).
The thing is, temporaries are rvalues. The standard states that non-const references can only be bound to lvalues. Therefore, a standards compliant compiler won't let you pass temporaries(rvalues) as arguments to non-const reference parameters. (This doesn't have anything to do with templates in particular, it's a general rule).
This is to the best of my knowledge, so take it with a bit of scepticism.