Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Const temporary from template type and why use std::add_const?

The following code is excerpted from cppreference.com.

#include <iostream>
#include <type_traits>

struct foo
{
    void m() { std::cout << "Non-cv\n"; }
    void m() const { std::cout << "Const\n"; }
};

template <class T>
void call_m()
{
    T().m();
}

int main()
{
    call_m<foo>();
    call_m<std::add_const<foo>::type>();
}

However, when compiled with VC++ Nov 2012 CTP, the output is

Non-cv

Non-cv

rather than the expected:

Non-cv

Const

Besides, what's the difference between the following two statements:

call_m<const foo>();

and

call_m<std::add_const<foo>::type>();

like image 601
xmllmx Avatar asked Feb 28 '13 12:02

xmllmx


1 Answers

This appears to be a bug with MSVC. Using an expression of the form T() (which is an explicit type conversion, as far as the standard is concerned) results in a prvalue of the specified type.

The expression T(), where T is a simple-type-specifier or typename-specifier for a non-array complete object type or the (possibly cv-qualified) void type, creates a prvalue of the specified type, which is value-initialized

It is only with non-class types that the const would be ignored, due to a rule that non-class prvalues cannot have cv-qualified types:

Class prvalues can have cv-qualified types; non-class prvalues always have cv-unqualified types.

So the temporary object created by T() here should be const and should therefore call the const member function.

As for when and why you would use std::add_const, we can take a look at the reason it was included in the proposal. It states that the add_const, add_volatile, add_cv, add_pointer, and add_reference type traits were removed from the proposal but then reinstated after complaints from users of Boost.

The rationale is that these templates are all used as compile time functors which transform one type to another [...]

The example given is:

// transforms 'tuple<T1,T2,..,Tn>'
// to 'tuple<T1 const&,T2 const&,..,Tn const&>'
template< typename Tuple >
struct tuple_of_refs
{
   // transform tuple element types
   typedef typename mpl::transform<
      typename Tuple::elements,
      add_reference< add_const<_1> > // here!
   >::type refs;
   typedef typename tuple_from_sequence<refs>::type type;
};

template< typename Tuple >
typename tuple_of_refs<Tuple>::type
tuple_ref(Tuple const& t)
{
    return typename tuple_of_refs<Tuple>::type(t);
}

You can think of mpl::transform as taking the compile-time metaprogramming equivalent to a function pointer as its second template argument - add_reference<add_const<...>> is applied to each of the types in Tuple::elements. This simply couldn't be expressed using const.

like image 69
Joseph Mansfield Avatar answered Nov 17 '22 01:11

Joseph Mansfield