简体   繁体   中英

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>();

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 .

From what i remember it goes as follows, you can write 3 consts(3 is the number of purpose) in a function declaration.

before the return type, after the function and its parameters, and on the parameters themselves.

const at the end of a function signature means that the function should assume the object of which it is a member is const. In practical terms it means that you ask the compiler to check that the member function does not change the object data in any way. It means asking the compiler to check that it doesn't directly change any member data, and it doesn't call any function that itself does not guarantee that it won't change the object.

before the return type means the thing the function is to return should be a const.

const parameter means that the parameter cannot be changed.

so the difference here is, the first call is not a const so it goes to the "non-cv", the second call is a const and so goes to "const".

what i think of why VC++ goes to the same function both times is that call_m explicitly calls for T().m() thinking it shouldnt go to the const one.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM