Difficulty declaring free function that takes parameter that is anested member of a class template

T

Travis Vitek

Can someone explain why the compiler is unable to deduce T when
invoking the function template `f' in the example below? Is there some
way to get the compiler to see `f' without having to explicitly
specify the template argument type?

template <class T>
struct A {
enum B {
};
};

template <class T>
void f(typename A<T>::B a)
{
}

int main ()
{
A<int>::B a0 = A<int>::B();
f(a0); // don't want to explicitly specify f<int>(a0) here
}

I have been able to move the definition of `f' into A to get the
results I want, but I'd like to declare/define the function outside of
A if possible. Is there something wrong with my declaration of `f', or
is there some other way to do what I want?

Travis
 
V

Victor Bazarov

Can someone explain why the compiler is unable to deduce T when
invoking the function template `f' in the example below?

The answer to that is usually, "Because that is not one of the contexts
from which the compiler is required to deduce the template arguments".
Is there some
way to get the compiler to see `f' without having to explicitly
specify the template argument type?

template<class T>
struct A {
enum B {
};
};

template<class T>
void f(typename A<T>::B a)
{
}

int main ()
{
A<int>::B a0 = A<int>::B();
f(a0); // don't want to explicitly specify f<int>(a0) here
}

I have been able to move the definition of `f' into A to get the
results I want, but I'd like to declare/define the function outside of
A if possible. Is there something wrong with my declaration of `f', or
is there some other way to do what I want?

Wrong? You'll have to define "wrong". A member of a template is not a
deducible context. The reason is rather benign, I think: there can be a
specialization (or partial, which means virtually unlimited number of
them) of your 'A' template which has no 'B' member, or a different 'B'
member, so the compiler would have to look through all specializations
of 'A' (the set of which is open at the time of compiling 'f(a0)', to
determine what 'T' is. That's asking too much, and thus the members of
template are not required to be a deducible context.

V
 
T

Travis Vitek

Wrong?  You'll have to define "wrong".  A member of a template is nota
deducible context.

Okay, that is what I am doing wrong. I'll have to read the standard to
find a way around this. That will probably answer my second
question... what can I do to make my declaration of `f' legal?

I have the following which appears to work.

template<class T>
struct A {
enum B {
};

friend void f(B);
};

int main ()
{
A<int>::B a0 = A<int>::B();
f(a0);
}

I don't really want to make `f' a friend of A if I don't have to, but
this is the only way I can see to get the functionality I want. I also
don't understand how I would define `f' outside of the body of A. I
realize I could define it in the body as an inline function, and maybe
that is what I'll have to do. I just want to know if I'm missing
something that would allow me to be able to define it out of line.

Travis
 
T

Travis Vitek

what can I do to make my declaration of `f' legal?

I suppose I should be a bit more specific. I _need_ to be able to call
`f' without specifying the template parameter. In my actual code, B is
a type that I want to be able to use with bitwise operators. More
succinctly...

template<class T>
struct A {
enum bitfield {
zero = 0,
one = 1,
two = 2,
four = 4
};

friend bitfield operator|(bitfield lhs, bitfield rhs);
friend bitfield operator&(bitfield lhs, bitfield rhs);
friend bitfield operator^(bitfield lhs, bitfield rhs);
friend bitfield operator~(bitfield rhs);

friend bitfield& operator|=(bitfield& lhs, bitfield rhs);
friend bitfield& operator&=(bitfield& lhs, bitfield rhs);
friend bitfield& operator^=(bitfield& lhs, bitfield rhs);
};

int main ()
{
A<int>::bitfield b = A<int>::eek:ne | A<int>::four;
}

Travis
 
V

Victor Bazarov

Okay, that is what I am doing wrong. I'll have to read the standard to
find a way around this. That will probably answer my second
question... what can I do to make my declaration of `f' legal?

I have the following which appears to work.

template<class T>
struct A {
enum B {
};

friend void f(B);
};

int main ()
{
A<int>::B a0 = A<int>::B();
f(a0);
}

I don't really want to make `f' a friend of A if I don't have to, but
this is the only way I can see to get the functionality I want. I also
don't understand how I would define `f' outside of the body of A. I
realize I could define it in the body as an inline function, and maybe
that is what I'll have to do. I just want to know if I'm missing
something that would allow me to be able to define it out of line.

I am not sure why (or how) declaring a friend function like you did
above provides you with a solution. I don't see the full code, I can't
try it to see the result. Is 'f' still a template? Do you get the
"right" 'f' instantiated in that case? Or do you just get no diagnostic
from your compiler?

The Standard actually says nothing about friends versus non-friends AFA
template argument deduction goes, so there should be no difference
(although I haven't checked the new Standard yet). Perhaps what you see
is not exactly what you need...

V
 
V

Victor Bazarov

I suppose I should be a bit more specific. I _need_ to be able to call
`f' without specifying the template parameter. In my actual code, B is
a type that I want to be able to use with bitwise operators. More
succinctly...

template<class T>
struct A {
enum bitfield {
zero = 0,
one = 1,
two = 2,
four = 4
};

friend bitfield operator|(bitfield lhs, bitfield rhs);
friend bitfield operator&(bitfield lhs, bitfield rhs);
friend bitfield operator^(bitfield lhs, bitfield rhs);
friend bitfield operator~(bitfield rhs);

friend bitfield& operator|=(bitfield& lhs, bitfield rhs);
friend bitfield& operator&=(bitfield& lhs, bitfield rhs);
friend bitfield& operator^=(bitfield& lhs, bitfield rhs);
};

int main ()
{
A<int>::bitfield b = A<int>::eek:ne | A<int>::four;
}

Your operator functions have two arguments. Are they supposed to be the
inner type of the same template? Or is it possible to do

auto b = A<float>::eek:ne | A<long>::four;

?

In similar situations when I've run into compiler's inability to deduce
template arguments, and especially when operators are concerned, the
solution would always be to have a named function, and (yes,
unfortunately) supply the template argument to it. IOW, something like

auto b = Op_or<int>(A<int>::eek:ne | A<int>::four);

You *could* of course have your 'one' and 'four' be of a particular
user-defined type (not 'enum') and define the operators *as members*.
In that case the name lookup should work differently and the template
argument deduction isn't really needed since you've already provided it
(when you write A<int>::eek:ne, that is).

V
 
T

Travis Vitek

I am not sure why (or how) declaring a friend function like you did
above provides you with a solution.

I'm not sure how to respond. Your response makes is sound like you're
confused about how or why the code works.
I don't see the full code, I can't
try it to see the result.  Is 'f' still a template?  Do you get the
"right" 'f' instantiated in that case?  Or do you just get no diagnostic
from your compiler?

The provided code compiles without diagnostic. If I add
implementations of each of the operators, they all behave as expected
and are called when they should be without explicitly specifying any
template type at the call site. i.e., it works.

Travis
 
V

Victor Bazarov

I'm not sure how to respond. Your response makes is sound like you're
confused about how or why the code works.

There is no indication in the Standard (or at least I can't find it)
that placing a declaration of your 'f' function as a friend into that
class template *should* help the compiler deduce the template argument.
Do you understand what I am writing here? If your compiler somehow
deduces the argument from the context beyond the listed in the Standard,
fine. The compilers are allowed to do that. It's called "a language
extension". It's not portable. It's not even guaranteed to be valid
code in the next version of the same compiler.

I would like to hear from language experts (especially those who deal
with templates more than I do) about this, but I've not seen anybody
chime it yet.
The provided code compiles without diagnostic. If I add
implementations of each of the operators, they all behave as expected
and are called when they should be without explicitly specifying any
template type at the call site. i.e., it works.

Good for you. Problem solved, then?

V
 
J

Joshua Maurice

There is no indication in the Standard (or at least I can't find it)
that placing a declaration of your 'f' function as a friend into that
class template *should* help the compiler deduce the template argument.
  Do you understand what I am writing here?  If your compiler somehow
deduces the argument from the context beyond the listed in the Standard,
fine.  The compilers are allowed to do that.  It's called "a language
extension".  It's not portable.  It's not even guaranteed to be valid
code in the next version of the same compiler.

I would like to hear from language experts (especially those who deal
with templates more than I do) about this, but I've not seen anybody
chime it yet.

If your analysis is right, wouldn't that interfere with function
resolution and function overloading, and maybe even hide functions?
That would break and/or change the meaning of well formed programs,
and that is not allowed as a "language extension".
 
T

Travis Vitek

There is no indication in the Standard (or at least I can't find it)
that placing a declaration of your 'f' function as a friend into that
class template *should* help the compiler deduce the template argument.

Sections [temp.inject] and [basic.lookup.koenig] appear to be closely
related to this issue.
  Do you understand what I am writing here?  If your compiler somehow
deduces the argument from the context beyond the listed in the Standard,
fine.  The compilers are allowed to do that.  It's called "a language
extension".  It's not portable.  It's not even guaranteed to be valid
code in the next version of the same compiler.

I find it hard to believe that all of the compilers I'm testing
implement the same 'language extension'...

HP aC++ 6.25
GNU gcc 3.4.6, 4.1.2, 4.3.4, and 4.4.5
Microsoft Visual Studio 10 and 11
Oracle SunPro C++ 5.10
IBM VisualAge C++ 11.1
I would like to hear from language experts (especially those who deal
with templates more than I do) about this, but I've not seen anybody
chime it yet.

I'm guessing I'll have to take it over to comp.lang.c++.moderated if I
want to hear from them.
Good for you.  Problem solved, then?

I suppose. It would still be nice to get some validation as to how/why
this code works, and how I might define the functions outside the body
of A.

Travis
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top