4

Is it a bug in Visual C++ 2010 or right behaviour?

template<class T>
T f(T const &r)
{
    return r;
}

template<class T>
T f(T &&r)
{
    static_assert(false, "no way"); //< line # 10
    return r;
}

int main()
{
    int y = 4;
    f(y); //< line # 17
}

I thought, the function f(T &&) should never be called but it's called with T = int &. The output:

    main.cpp(10): error C2338: no way
          main.cpp(17) : see reference to function template instantiation 'T f(T)' being compiled
          with
          [
              T=int &
          ]

Update 1 Do you know any C++x0 compiler as a reference? I've tried comeau online test-drive but could not compile r-value reference.

Update 2 Workaround (using SFINAE):

#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_reference.hpp>

template<class T>
T f(T &r)
{
    return r;
}

template<class T>
typename ::boost::disable_if< ::boost::is_reference<T>, T>::type f(T &&r)
{
    static_assert(false, "no way");
    return r;
}

int main()
{
    int y = 4;
    f(y);
    // f(5); // generates "no way" error, as expected.
}

Update 3 Some of compilers trigger on static_assert(false, "no way") even if no function template instantiation. Workaround (thanks to @Johannes Schaub - litb)

template<class T> struct false_ { static bool const value = false; };
...
static_assert(false_<T>::value, "no way");

or

static_assert(sizeof(T) == sizeof(T), "no way");
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
Sergey Shandar
  • 2,357
  • 18
  • 25
  • 1
    I wonder if it's the peculiarity of Visual C++ that a `static_assert(false, ...)` is not always triggered? With G++, the assertion would have to be template argument dependent not to trigger unless function is instantiated. – UncleBens Feb 18 '11 at 19:32
  • I think it is a Visual C++ "feature". Use 'static_assert(sizeof(T) == sizeof(F), "no way")' for other compilers. – Sergey Shandar Feb 18 '11 at 22:51
  • Non-compliance is certainly not a feature. Anyway, `sizeof(T) == 0` is a good way to make the assertion always fail, but be dependent. (I have an `always_false::value` template lying around.) – GManNickG Feb 18 '11 at 23:11
  • @GMan Agreed. I wrote "feature" in quotes. A kind of humour. – Sergey Shandar Feb 19 '11 at 00:10
  • 1
    I've added an answer pointing that out. Since the question is why the static_assert fires, I think it's a good idea to have an answer static that problem. – Johannes Schaub - litb Feb 26 '11 at 09:18

2 Answers2

5

As I understand it (and I may not be completely right; the specification is a bit complicated), the template type deduction rules conspire against you.

The compiler first attempts to substitute all templates (it's not choosing at this point yet—just looking for options) and gets:

  • T const &r matches int lvalue with T = int, creating f(int const &)
  • T &&r matches int lvalue with T = int& and int & && reduces to int&, creating f(int &) (there are rules saying this in the spec).

Now it comes to selecting correct overload and the later is better match, because the first differs in cv-qualification and the later does not. That's also the reason why when you remove the const, you get ambiguous overload error—the overloads end up being exactly the same.

Ad Update1: gcc supports many of the C++0x features. You can get native windows build from mingw or use cygwin.

Ad Update2: If you really need separate overloads for rvalue and lvalue, that seems to be the only option. But most templates do the right thing with just any kind of reference, perhaps using std::forward to ensure proper resolution of functions they call depending on whether they got rvalue or lvalue).

Jan Hudec
  • 73,652
  • 13
  • 125
  • 172
3

Your fix doesn't solve the problem with static_assert firing though. The static_assert(false, ...) will still trigger for compilers that parse templates at definition time (most do).

They will see that any function template instantiation will be ill-formed, and the Standard allows them to issue an error for the template itself then, and most will do so.

For making this work you need to make the expression dependent so that the compiler doesn't know when it parses the template that it will always evaluate to false. For example

template<class> struct false_ { static bool const value = false; };

template<class T>
T f(T &&r)
{
    static_assert(false_<T>::value, "no way"); //< line # 10
    return r;
}
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212