12

I have the following code, which I cannot get to work:

struct foo {};
foo foo1 = {};

template <foo& F>
class FooClass {};

template <foo& F>
void foobar(FooClass<F> arg) {
}

int main() {
    FooClass<foo1> f;
    foobar(f);
}

The error is:

main.cpp:14:5: error: no matching function for call to 'foobar'

note: candidate template ignored: substitution failure : deduced non-type template argument does not have the same type as the its corresponding template parameter ('foo' vs 'foo &')

Is it at all possible to infer lvalue reference template parameters? If so, how should it be done?

Community
  • 1
  • 1
molf
  • 73,644
  • 13
  • 135
  • 118
  • The error message even contains a typo xD "as the its". Probably not a very common scenario. But I doubt that two compilers get it wrong? (It's rejected by both gcc6.0 and clang3.9) – dyp Feb 01 '16 at 20:57
  • Isn't it that template arguments must be constant expressions...? – 101010 Feb 01 '16 at 20:59
  • 3
    @101010 Since the argument is a global variable, the "address" of that variable is a constant expression. Or rather, it has a fixed symbol name that can be used to instantiate the template. Similarly, references to variables of static storage duration can be used as template arguments. – dyp Feb 01 '16 at 21:02
  • 2
    This is probably [temp.deduct.type]p17, but it seems overly restrictive in this case: The (observable) type of `foo1` as an expression is `foo` and therefore does not match the type `foo&` of the template parameter `F` exactly. Might be a defect in the Standard. – dyp Feb 01 '16 at 21:08
  • 3
    Fwiw, my type_name function (http://stackoverflow.com/a/20170989/576911) reports the type of `f` as `FooClass` on both clang and gcc. This represents independently written versions of `__cxa_demangle` as specified here: http://mentorembedded.github.io/cxx-abi/abi.html#mangling – Howard Hinnant Feb 01 '16 at 21:52
  • 3
    VS-2015 reports `f` has type `class FooClass<&struct foo foo1>`. **And** it successfully compiles this example. – Howard Hinnant Feb 01 '16 at 21:57
  • @101010 see [tem.arg.nontype]/1 . The wording is that the argument must be a "constant expression [...] except that the `&` may be omitted if the template-parameter is a reference" . This is rather cumbersome but it's trying to saying that you can use the name of any object with static storage duration (because applying `&` to such an object would produce a constant expression) – M.M Feb 02 '16 at 00:24
  • @dyp Ts ts, you should've checked the database immediately! :P – Columbo Feb 02 '16 at 17:38
  • @molf: *"... Easy to fix, right? Just prepend an `&`:..."*??? How could prepending `F` with `&` (i.e. turning it into a *pointer* expression) possibly fix anything here? – AnT stands with Russia Feb 02 '16 at 17:47
  • @AnT Yes, I realised that after writing it. – molf Feb 02 '16 at 17:54
  • @Columbo I did, but obviously not thoroughly enough. – dyp Feb 02 '16 at 18:23

1 Answers1

6

This is precisely covered by CWG 2091:

According to 14.8.2.5 [temp.deduct.type] paragraph 17,

If P has a form that contains <i>, and if the type of the corresponding value of A differs from the type of i, deduction fails.

This gives the wrong result for an example like:

template<int &> struct X;
template<int &N> void f(X<N>&);
int n;
void g(X<n> &x) { f(x); }

Here, P is X<N>, which contains <i>. The type of i is int&. The corresponding value from A is n, which is a glvalue of type int. Presumably this should be valid.

I think this rule means to say something like,

If P has a form that contains <i>, and the type of i differs from the type of the corresponding template parameter of the template named by the enclosing simple-template-id, deduction fails.

As noted by @dyp, [temp.deduct.type]/17 should be more permissive. In your example, the argument in FooClass<F> (F) does not have reference type - it's an lvalue of type foo. The template parameter of FooClass is a reference. The DR was resolved last year.

Columbo
  • 60,038
  • 8
  • 155
  • 203