views:

209

answers:

4

Here is a tricky situation, and i wonder what ways there are to solve it

namespace {
  template <class T> 
  struct Template { /* ... */ }; 
}

typedef Template<int> Template;

Sadly, the Template typedef interferes with the Template template in the unnamed namespace. When you try to do Template<float> in the global scope, the compiler raises an ambiguity error between the template name and the typedef name.

You don't have control over either the template name or the typedef-name. Now I want to know whether it is possible to:

  • Create an object of the typedefed type Template (i.e Template<int>) in the global namespace.
  • Create an object of the type Template<float> in the global namespace.

You are not allowed to add anything to the unnamed namespace. Everything should be done in the global namespace.

This is out of curiosity because i was wondering what tricks there are for solving such an ambiguity. It's not a practical problem i hit during daily programming.

A: 

I know it somewhat spoils your point, but I really think the main trick is to avoid something like this like the plague.

sbi
A: 

It's possible to access the global typedefed template by being explicit about the namespace, ie

::Template a

is a Template<int> from the anonymous namespace. Not sure if you can get a Template<float>.

Surprisingly Clang's C++ compiler is fine with the following, probably not standard behaviour:

#include <iostream>
namespace {
    template <class T>
        struct Template {T value;};}
typedef Template<int> Template;

int main(){    
    ::Template a;
    Template<float> b;
    a.value = 6;
    b.value = 3.14;
    std::cout<<a.value<<" "<<b.value<<"\n";
}
Scott Wales
Yep, clang is not correct with accepting that. Comeau and GCC forbid that too :) Good find, will you report it or should i do it?
Johannes Schaub - litb
I have reduced it to this testcase: http://codepad.org/pnrSlhPq
Johannes Schaub - litb
@Johannes thanks, reported as http://llvm.org/bugs/show_bug.cgi?id=7251
Scott Wales
Since you are playing a language lawyer, can you find the quote from the standard that requires this case to be treated as artificially ambiguous?You have clear example that it's not ambiguous and that compiler has efficient and systematic way of resolving it. So if you do find a quote from the standard then it would be more reasonable to at least try to file a bug against the standard rather than bug the compiler which has efficient resolution.
ZXX
A: 

disclaimer: I don't know why you'd want to do this and would probably speak sternly to someone who did.

namespace
{
   template <typename T> class Template { };
}

typedef Template<int> IntTemplate;
typedef Template<float> FloatTemplate;
typedef IntTemplate Template;

int main() {
    ::Template t;
    FloatTemplate ft;
}
dash-tom-bang
Hmm, the `typedef Template<int> Template;` is gone somehow in your answer :) It was meant to be one block. Everything should be added after the fact. That's why it's so difficult to do :)
Johannes Schaub - litb
Oh I thought the requirement was only that stuff inside the namespace couldn't be changed. Basically, whatever you're trying to do is incorrect. It doesn't make any sense to hide an "internal" thing and then try to have both the old and new definitions available.
dash-tom-bang
@dash it totally makes no sense. it's a language lawyer question. It's not connected to any senseful piece of software :)
Johannes Schaub - litb
+1  A: 

Using C++0x:

namespace {
  template<class T> struct Template { };
}
typedef Template<int> Template;

#include<iostream>

template<typename T> 
void PrintType() { 
    std::cout << __PRETTY_FUNCTION__ << std::endl; 
}

template<typename FullType, typename NewParameter>
class Rebind {
  template<template<class> class Template, typename OldParameter>
  static Template<NewParameter> function(Template<OldParameter>);

public:
  typedef decltype(function(FullType())) NewType;
};

int main()
{
  PrintType< ::Template>();
  PrintType<Rebind< ::Template, float>::NewType>();
  return 0;
}

With gcc45 that yields

void PrintType() [with T = <unnamed>::Template<int>]
void PrintType() [with T = <unnamed>::Template<float>]

Apparently it compiles with Cormeau, but I only have access to their online test, so I'm stuck just assuming it functions as expected.

I couldn't figure out any way to pass an actual type to a struct directly and have it degrade into a template type, but the compiler had no problems stripping the two when it had to guess at function parameters. Maybe this works in C++03 using boost::result_of instead of decltype, but I've never used it before so I figured I'd stick to what I know.

Note the spacing within main. Rebind<::Template, float>::NewType gets gobbled by the parser because of <: being a digraph. I think it gets turned into Rebind[:Template, float>::NewType. So the space before ::Template is vital.

As an aside, I had no idea nested template parameters couldn't use typename [template<template<typename> class T> rather than template<template<typename> typename T>]. I think I relearn that every time I try to remember the syntax for the construct.

Dennis Zickefoose
Ah your answer made me think of a second way to solve this question in C++03. You are close of using that second way to solve it, actually... :)
Johannes Schaub - litb