1

This is a question related to OP's solution to Is constexpr useful for overload.

Basically, he used

template<class T>
typename std::enable_if<std::is_arithmetic<T>::value, int>::type
f(T&& n) { ... }

and

template<class T>
typename std::enable_if<!std::is_arithmetic<T>::value, int>::type
f(T&& n) { ... }

to know whether f() has been called with is a compile-time variable (e.g. literal: f(42)) or an lvalue (e.g. local variable: f(argc)) as its argument.

Q: How does that work ? (I expected, in both calls, that the first overload would be called (i.e. std::is_arithmetic<T>::value == true)

Here is a full example:

Run It Online

#include <iostream>
#include <type_traits>
using std::cout;
using std::endl;

template<class T>
constexpr
typename std::enable_if<std::is_arithmetic<T>::value,
                        int>::type
inline f(T&& n)
{
    //cout << "compile time" << endl;
    return 1;
}

template<class T>
typename std::enable_if<!std::is_arithmetic<T>::value,
                        int>::type
inline f(T&& n)
{
    //cout << "run time" << endl;
    return 0;
}

int main(int argc, char* argv[])
{
    const     int rt = f(argc);
    constexpr int ct = f(42);

    cout << "rt: " << rt << endl;
    cout << "ct: " << ct << endl;
}
maddouri
  • 3,737
  • 5
  • 29
  • 51
  • Your first snippet is not really correct, is it? What if T is deduced to an lvalue reference? (Edit: Oh, that's what this question is effectively about...) – Columbo Oct 18 '15 at 10:52
  • The whole point of my question is that I don't understand how [this code works](http://coliru.stacked-crooked.com/a/fd3400aa4b6b9e68). To answer your comment: I don't know how (or even if) the _lvalueness_ would influence `std::is_arithmetic::value` – maddouri Oct 18 '15 at 10:59
  • 1
    If an lvalue is passed, `T` is deduced to an lvalue reference type, which is not an arithmetic type (thus the trait yields `false`). You'll have to read up on forwarding references. – Columbo Oct 18 '15 at 11:01
  • 1
    In other words, the code doesn't do at all what the OP in that other question thinks it does. Asking why it works like that when it doesn't actually work like that (not your fault at all) is unlikely to give you useful answers. –  Oct 18 '15 at 11:03
  • Thanks for the comments, I'll google some articles on this subject. (I'll point out that my question was about "how" not "why" though ;) ) – maddouri Oct 18 '15 at 11:09

1 Answers1

1

A template function of the form

template <typename T>
void func(T&& t);

looks as if it takes an r-value reference. But in actual fact T&& here is what Scott Meyers calls a universal reference, otherwise known as a forwarding reference. Different things can happen depending on the value category of the argument. Let's have a look at each case:

  1. t is a non-const lvalue, for example

    int i = 0;
    func(i);
    

    In this case, T is deduced to be an lvalue reference to int, that is, T=int&.

  2. t is a const lvalue, for example

    const int i = 1;
    func(i);
    

    Similarly, in this case T is deduced to be const int&.

  3. t is an rvalue, for example

    func(1);
    

    In this case, T is deduced to be int just as we might have expected

Exactly why these deductions happen this way is to do with the rules for reference collapsing; I highly recommend reading Scott Meyers' article on the subject if you're interested.

The last case above also illustrates the point that in C and C++, literals (except string literals) are always rvalues.

What does this have to do with the enable_if? Well if your f is called with an integer literal, then T is deduced to be plain int. Obviously, is_arithmetic<int> is true, so the second function gets SFINAE'd out and the first is called.

However, when called with an lvalue, T is deduced to be (const) int&. A reference is not arithmetic, so the first function disappears leaving only the second to be called.

Tristan Brindle
  • 16,281
  • 4
  • 39
  • 82
  • Cool, thanks for explaining! I'll have a look at [Scott's article](https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers) _(seems more readable than the standardese in [temp.deduct.call] :) )_ – maddouri Oct 18 '15 at 11:39