5

Why isn't it possible to pass std::cout's address as template argument? Or if it is possible then how?

Here is what I tried:

#include <iostream>

template<std::ostream* stream>
class MyClass
{
public:
    void disp(void)
        { (*stream) << "hello"; }
};

int main(void)
{
    MyClass<&(std::cout)> MyObj;
    MyObj.disp();

    return 0;
}

And the error message I got from clang++ -std=c++11 :

main.cpp:15:11: error: non-type template argument does not refer to any declaration
        MyClass<&(std::cout)> MyObj;
                 ^~~~~~~~~~~
main.cpp:6:24: note: template parameter is declared here
template<std::ostream* stream>
                       ^
1 error generated.

and from g++ -std=c++11 :

main.cpp: In function ‘int main()’:
main.cpp:15:22: error: template argument 1 is invalid
  MyClass<&(std::cout)> MyObj;
                      ^
main.cpp:15:29: error: invalid type in declaration before ‘;’ token
  MyClass<&(std::cout)> MyObj;
                             ^
main.cpp:16:8: error: request for member ‘disp’ in ‘MyObj’, which is of     non-class type ‘int’
  MyObj.disp();
        ^

Any ideas?

Gael Lorieul
  • 3,006
  • 4
  • 25
  • 50
  • 4
    Why did you parenthesize it? Also fix the access level. – LogicStuff Sep 29 '16 at 17:24
  • 1
    Try it without the parentheses: `MyClass<&std::cout> MyObj;` – aschepler Sep 29 '16 at 17:26
  • 1
    I'd like to see an explanation on how this is parsed, though. – LogicStuff Sep 29 '16 at 17:28
  • 1
    Indeed: it works without the parenthesis! But Why? I tend to put parenthesis quite often especially in cases like `&object.subobject` where it doesn't seem clear to me whether the ampersand is going to apply to `object` or to `subobject` (although I'm sure the standard must lift that ambiguity)… I do agree however that there was no such ambiguity in the present case… – Gael Lorieul Sep 29 '16 at 17:38
  • 1
    @GLorieul: `()` makes it an expression that requires runtime evaluation (more or less). Just throwing `()` everywhere without understanding what it does is a bad idea! – Lightness Races in Orbit Sep 29 '16 at 17:46
  • 1
    @LightnessRacesinOrbit oh oh, now I see… Would you have a link to a reference that explains that in more detail ? – Gael Lorieul Sep 29 '16 at 17:50
  • 1
    I don't think that's a dup. The Standard has language explicitly saying `&X::m` is required for pointers to member, but I can't find anything that applies here. – aschepler Sep 29 '16 at 17:51
  • 1
    @aschepler I at least think these Q&A are very closely related. The dup contains a standard cite however. Vote to reopen if you think this questin needs to be kept standalone. – πάντα ῥεῖ Sep 29 '16 at 17:53
  • 2
    Following @πάντα ῥεῖ 's link, I found a [nice answer](http://stackoverflow.com/a/7138582/3492512) – Gael Lorieul Sep 29 '16 at 17:53

2 Answers2

5

Before C++17 removed this restriction, the syntactic form of a template argument for a pointer or reference template parameter was restricted. N4140 [temp.arg.nontype]/1.3 says that it must be

expressed (ignoring parentheses) as & id-expression, where the id-expression is the name of an object or function, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference

(std::cout) isn't an id-expression. It's a primary-expression.

The "(ignoring parentheses)" part was added by Core issue 773, and is apparently meant to permit (&i), not &(i).

Columbo
  • 60,038
  • 8
  • 155
  • 203
T.C.
  • 133,968
  • 17
  • 288
  • 421
2

This fixes your code, omit the parenthesis:

#include <iostream>

template<std::ostream* stream>
class MyClass
{
public:
    void disp(void) { 
        (*stream) << "hello"; 
    }
};

int main(void)
{
    MyClass<&std::cout> MyObj;
    MyObj.disp();

    return 0;
}

Live Demo


A more detailed explanation why can be found here:

Error with address of parenthesized member function

Community
  • 1
  • 1
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
  • Thanks for the link! ;) – Gael Lorieul Sep 29 '16 at 17:55
  • @GLorieul Note that `&std::cout` isn't a function pointer at all, but the address of a global instance. – πάντα ῥεῖ Sep 29 '16 at 18:13
  • 1
    The "explanation" is bogus. We are not forming a pointer to member here. – T.C. Sep 29 '16 at 19:38
  • @πάνταῥεῖ yes indeed `std::cout` is an instance of class `std::ostream` accessible globally (i.e. [declared as](http://en.cppreference.com/w/cpp/io/cout) `extern std::ostream std::cout`). Yet the same problem as for member functions arise here (as I assume you suggested) – Gael Lorieul Sep 30 '16 at 06:54