2

Note: the following code is illegal, but a conforming compiler is not required to reject it (and some don't).

In a library I'm working with I have a template function declaration for Foo and a template function definition for Bar in foobar.h:

template<class C> int Foo();

template<class C> int Bar() {
  return Something(  Foo<C>()  );
}

The intent is that other code would be able to use it like this:

#include "foobar.h"

int main() {
  Bar<MyClass>();
  return 0;
}

// Ideally, the usage (above) and the definition (below)
// would/could be in different translation units. 

template<> int Foo<MyClass>() { return 5; }

The question: Is there a way to make this work that is also legal?


The issue is (if I'm understanding things correctly) that despite compiling, this is technically illegal: it violates the ODR because both the explicit specialization and the usage of Bar<MyClass> count as definitions, despite the fact that there is no body to work with in the usage case.

The reasons I want to use this pattern to parameterize Foo is that, as a result of the style guide I'm required to follow, the only way to ensure that anything is lexically included before the definition of Bar is for it to be included by foobar.h. But (for reason I expect I don't need to explain) that's a non-starter.

BCS
  • 75,627
  • 68
  • 187
  • 294
  • What is that last definition of `Foo` supposed to mean? It is absolutely not clear what you are trying to do. Provide more details. – AnT stands with Russia May 03 '11 at 20:42
  • Why not supply an `int(*)(void)` function pointer as a template parameter to `Bar`, instead of the class C? `template int Bar() { return Something(FOO()); }`. Then the user can indicate what function to call directly, rather than indirectly through specializing `Foo` for `MyClass`. And then the function pointer could be replaced by a functor class if preferred. – Steve Jessop May 03 '11 at 20:44
  • I think free function templates should usually be declared `inline` to circumvent the ODR. – Philipp May 03 '11 at 20:56
  • Not sure about ODR, but the code seems to violate 14.7.3/6: "If a template ... is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place..." – Alexey Kukanov May 03 '11 at 21:30
  • @Steve Jessop: The type parameter is in effect a tag. Think of it as: "the Bar for `MyClass`" or "The Bar for whatever class I pass in". – BCS May 03 '11 at 21:36
  • 2
    A thought: you do not have the specialization to be included before the definition of Bar, just before the first use of Bar should be enough because the instantiation point is there. I.e. having the specialization being declared (not even defined) before `main()` would be legal I believe. Not an option too? – Alexey Kukanov May 03 '11 at 21:43
  • @Alexey: that would make for very brittle code. – BCS May 03 '11 at 21:49
  • @Alexey: Regarding your quote of 14.7.3/6, there is no implicit instatiation of `Foo` happening, so no violation. – quamrana May 03 '11 at 21:51
  • @BCS: Why would providing the specialization `Foo` before the use of `Bar` make for brittle code? The normal thing to do would be to have the specialization `Foo` right after `MyClass` is defined (in the same header file). – Paul Groke May 03 '11 at 22:49
  • @quamrana: of course `Foo` is implicitly instantiated, (via the instantiation of `Bar`). – Paul Groke May 03 '11 at 22:52
  • @pgroke: Any code that depends on the order of decelerations and use *inside* a file is brittle. The style rules I'm under amount to: all declarations that code in a file depend on should be in a header that is included at the top of the file (this is required of header files as well) -- Also, you are assuming that the explicit specialization would be in a header file where as I'd rather not have that be the case. – BCS May 03 '11 at 23:42
  • @pgroke: How can `Foo` be instantiated when there is no definition? – quamrana May 04 '11 at 08:02
  • @quamrana: the relevant point is that calling a template function doesn't require that the function be instantiated (at that point), just as calling a non template function doesn't require more than a prototype. All the compiler requires is enough info to generate the correct name-mangled symbol reference and the linker handles the rest (assuming the symbol is defined somewhere else. – BCS May 04 '11 at 15:29
  • I wonder if this is now possible using "extern templates" in C++ '11. – Richard Corden May 04 '11 at 17:25
  • @Richard Corden: if extern templates have anything to do with exported templates, I want nothing to do with them. (BTW, shouldn't that be C++0xB?) – BCS May 04 '11 at 17:31
  • @BCS: 'exported templates' have been deprecated. My layman understanding of 'extern templates' is that they stop implicit instantiations from taking place. Hence my reason for asking if this solves the problem here. – Richard Corden May 04 '11 at 17:44
  • @BCS: Yes, that's what I'm trying to say - but you're saying that its not legal. I thought that a template function 'prototype' would be exactly the same as a non-inline ordinary function prototype - the compiler just generates the correct name-mangled symbol reference and the linker sorts it later. The compiler doesn't have access to the source of the template function at the time. My answer now has two examples of this that seem to compile and run. – quamrana May 05 '11 at 08:20

3 Answers3

3

Is there a way to make this work that is also legal?

Yes, declare the specialization before it is used. Simply swapping the order of the specialization and main in your file would do this in the example you provided. However, in the example, you would still have to make sure no other TU used the specialization without declaring it.

Generally these specializations should be declared in a header.

Within what appear to be the confines of your example (i.e. no radical changes), there is no other way to make this work.

Fred Nurk
  • 13,952
  • 4
  • 37
  • 63
  • That could be made to work in the case I ran into this from, but doesn't generalize very well. (see the comment I added in the 2nd code block) – BCS May 04 '11 at 17:28
  • @BCS: The "ideal" situation in your comment is flatly impossible: things must be declared before they can be used, and the declaration for this function cannot be missing in a TU where it is used. – Fred Nurk May 04 '11 at 17:52
  • @BCS: Compare to the situation of trying void f() { this_function_not_declared(); } without declaring the called function in that TU. – Fred Nurk May 04 '11 at 17:52
  • Yes they must be declared before they are used, but what I want for an ideal solution is to use a template function that has only (and, in that TU, will only) be declared *but not **defined***. – BCS May 04 '11 at 18:29
  • @BCS: That is fine. Declare before used, define it once. – Fred Nurk May 04 '11 at 18:30
  • If that was all there was to it, the above code, as is, would be legal. It is not. – BCS May 04 '11 at 18:32
  • @BCS: The code in your question implicitly uses the Foo specialization before it is declared. Don't do that. Declare it before using it. – Fred Nurk May 05 '11 at 00:27
1

It is not legal, since it is not possible to specialize template functions.

You could do something like this :

template< typename T >
struct foo
{
  static int doSomething() {return 0;}
};
template< >
struct foo<int>
{
  static int doSomething() {return 5;}
};
BЈовић
  • 62,405
  • 41
  • 173
  • 273
1

This compiles links and runs with both VS2008 and gcc 4.5.2:

template<class C> int Foo();

int Something(int i ){ return i; }

template<class C> int Bar() {
    return Something(  Foo<C>()  );
}

class MyClass{};
class FooClass{};

// Update:
// Declaration of a specialisation.
template<> int Foo<MyClass>();

int Zoo(){
    return Something(  Foo<MyClass>()  );
}

#include <iostream>

int main() {
    std::cout << Bar<MyClass>() << std::endl;
    std::cout << Zoo() << std::endl;
    std::cout << Bar<FooClass>() << std::endl;
    return 0;
}

// Definitions of specialisations.
template<> int Foo<MyClass>() { return 5; }
template<> int Foo<FooClass>() { return 6; }

The output is:

5
5
6

The reason this works is that despite only being declared, template function Foo does have the two required definitions available to the linker.
At compile time, when the compiler sees the definition of main, it can create specialisations of Bar, but cannot create specialisations of Foo. In this case it just creates function calls to Foo<MyClass>() and Foo<FooClass>().
Later on the compiler finds specialisations of Foo which it compiles, leaves in the object file, but does nothing else with at the time. When the linker runs it finds everything it needs.

Update:
So I don't know why this is illegal, but for some reason it does seems to compile and run.

quamrana
  • 37,849
  • 12
  • 53
  • 71
  • 1
    Your description is exactly the behaver I want. OTOH, while it may compile and run, it is illegal code: http://permalink.gmane.org/gmane.comp.compilers.llvm.bugs/10472 I think both GCC and clang will reject it. – BCS May 03 '11 at 21:26
  • @BCS: I think this code *is* legal because there is no definition of `Foo` when main is compiled. If there is, gcc 4.5.2 comes back with a straightforward multiple definition error. – quamrana May 03 '11 at 21:45
  • If Bar is un-templatized and the Foo call is made to directly specify the type then this generates an error message (I.e. it's illegal). Unless I'm missing something, that shouldn't make a difference as far as legality goes (i.e. both are illegal) because either way the Foo call should be processed before the Foo definition. – BCS May 03 '11 at 21:58