Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delayed evaluation of template type function

I read "The C++ Programming language 4th edition, 1st printing, by Bjarne Stroustrup" book (from Amazon.com). Page 785. Stroustrup is explaining how he can eliminate explicit writing of "::type", when using "std::conditional + std::make_unsigned", using "type aliases" (keyword "using"). But that using "type aliases" on "std::conditional + std::make_unsigned" is causing compile errors. By now everything is as it should be. And he goes on to show how to elimintate these compile errors using "delayed evaluation of template type function".

The question is on the line Atype<make_unsigned<string> and myType2<string> ....

I used g++ 4.8.2.

Compile:

g++ -std=c++1y test45.cpp -o a

#include <type_traits>
#include <string>
#include <iostream>
#include <typeinfo>  // for typeid(...)
using namespace std;

template<class T>
struct ErrIndicator {
   typedef ErrIndicator<T> type;
};

template<bool C, class T, class F>
using Conditional = typename conditional<C,T,F>::type;

template<typename T>
using Make_unsigned = typename make_unsigned<T>::type;

template<template<typename ...> class F, typename... Args>
using Delay = F<Args ...>;

template<class T>
using myType1 = Conditional<is_integral<T>::value,
    Make_unsigned<T>,
    ErrIndicator<T>
    >;

template<class T> 
    using myType2 = Conditional<is_integral<T>::value,
    Delay<Make_unsigned, T>,   // delayed evaluation
    ErrIndicator<T>
    >;

template<class T>
    using myType4 = Conditional<is_integral<T>::value,
    make_unsigned<T>,
    ErrIndicator<T>
    >;

template<typename T>
class Atype {}; 

template<typename T>
void func1(T &ia /* output param */) {
  cout << "unsigned integral type" << endl;
  ia = 4;  // "unsigned integral type" computation
}

template<typename T>
void func1(ErrIndicator<T> &) {
  cout << "non integral type: " << typeid(T).name() << endl;
}

int main() {
  myType1<int> var1a; // OK
  // myType1<string> var1b; // Error; The book says error
  //                    // should occur here. Here I understand.
  myType2<int> var2a; // OK
  // myType2<string> var2b; // Error - why?. Maybe I didn't get it,
  //                      // but I understand the book as no
  //                      // error should occur here.
  //                      // @DyP answered it.
  Atype<make_unsigned<string> > var3;  // OK here, look below at @DyP
  //                             // for "foo, bar, X" why
  //                        // make_unsigned<string> is not an error here.
  // make_unsigned<string> var6; // Error
  // Atype<make_unsigned<string>::type > var4;  // Error
  Atype<make_unsigned<int>::type > var5;  // OK
  //-------------
  myType4<string>::type var7;    // Look below for "myType3", where @Yakk
  //                          // obviates the necessity to write "::type".
  // rsl7 = 1:
  cout << "rsl7 = " << is_same<decltype(var7), ErrIndicator<string> >::value << endl; 
  func1(var7);  // "non integral type" overload of func1()
  //---------
  myType4<int>::type var8;
  // rsl8 = 1:
  cout << "rsl8 = " << is_same<decltype(var8), unsigned int>::value << endl; 
  func1(var8);  // "unsigned integral type" overload of func1()
}
like image 662
vlakov Avatar asked Dec 31 '13 04:12

vlakov


People also ask

Are templates evaluated at compile time?

1.1.2 Using Function Templates A template is visited twice by the compiler. On first pass it's simply checked for correct syntax. It's only actually compiled when it is used (instantiated) in code.

Can a non template class have a template function?

A non-template class can have template member functions, if required. Notice the syntax. Unlike a member function for a template class, a template member function is just like a free template function but scoped to its containing class.


1 Answers

I think Stroustrup intents to delay the access to make_unsigned<T>::type, because this nested type isn't defined for non-integral types. However, using an alias template seems not to be enough for clang++ and g++: They resolve Delay<Make_unsigned,T> directly to Make_unsigned<T>, and this to make_unsigned<T>::type.

The whole example is:

template<typename C, typename T, typename F>
using Conditional = typename std::conditional<C,T,F>::type;

template<typename T>
using Make_unsigned = typename std::make_unsigned<T>::type;

// the example
Conditional<
  is_integral<T>::value,
  Delay<Make_unsigned,T>,
  Error<T>
>

// "The implementation of a perfect `Delay` function is nontrivial,
//  but for many uses this will do:"
template<template<typename...> class F, typename... Args>
using Delay = F<Args...>;

The question is of course, when is Delay<Make_Unsigned,T> resolved? For class templates (not alias templates), they're only instantiated implicitly when a complete object type is required or the semantics of the program are affected. Consider:

#include <type_traits>
using namespace std;

template<class T>
struct foo
{
    static_assert(is_same<T, void>{}, "!");
};

template<class X>
struct bar
{
    // without the line below, no error!
    //X x;
};

int main()
{
    bar<foo<int>> b;
}

This however is not the case for alias templates. They're substituted [temp.alias]/2

When a template-id refers to the specialization of an alias template, it is equivalent to the associated type obtained by substitution of its template-arguments for the template-parameters in the type-id of the alias template.

IMHO, this suggests that in the example above, Delay<Make_unsigned,T> is equivalent to make_unsigned<T>::type, which will instantiate make_unsigned<string>::type and cause a compile-time error.

like image 57
dyp Avatar answered Sep 29 '22 13:09

dyp