This code reproduces the problem with gcc-4.x, but compiles fine with prior
versions of gcc and with other compilers. The error output differs depending on
what exactly is parsed, but I believe this example illustrates the general
change in behavior.
FILE: header1.h
--
#ifndef _INCLUDED_Header1_H
#define _INCLUDED_Header1_H
#include
namespace N
{
const std::string & resolveName(const int &)
{
static const std::string & name = "int";
return name;
}
const std::string & getName(const int & v)
{
return resolveName(v);
}
template
const std::string & getName(const ValueType & v)
{
return resolveName(v);
}
}
#endif//_INCLUDED_Header1_H
--
FILE: main.cpp
--
#include
#include "header1.h"
class A
{
public:
A() { }
~A() { }
};
namespace N
{
const std::string & resolveName(const A &)
{
static const std::string name = "A";
return name;
}
}
int main(int, char **)
{
int result = 0;
const std::string & resultName = N::getName(result);
A a;
const std::string & name = N::getName(a); // <- fails to find
N::resolveName(const A &)
// while processing
// template const std::string & getName(const ValueType
&) from
// header1.h unless the above N::resolveName(const A &) is declared BEFORE
// header1.h is included, though the point of resolution appears AFTER
// both header1.h is included and N::resolveName(const A&) is defined.
// This behavior is new to gcc-4 and not present in most other compilers
// including gcc prior to version 4.
return result;
}
--
g++ -c main.cpp
header1.h: In function ‘const std::string& N::getName(const ValueType&)
[with ValueType = A]’:
main.cpp:26: instantiated from here
header1.h:21: error: invalid initialization of reference of type ‘const
int&’ from expression of type ‘const A’
header1.h:8: error: in passing argument 1 of ‘const std::string&
N::resolveName(const int&)’
Changing main.cpp to include header1.h AFTER resolveName(const A &) is declared
works around this issue:
FILE: main.cpp
--
#include
class A
{
public:
A() { }
~A() { }
};
namespace N
{
const std::string & resolveName(const A &)
{
static const std::string name = "A";
return name;
}
}
#include "header1.h"
int main(int, char **)
{
int result = 0;
const std::string & resultName = N::getName(result);
A a;
const std::string & name = N::getName(a);
return result;
}
--
In this contrived example, the workaround is straightforward and easy to
implement. In a larger codebase, the workarounds are difficult to implement or
even discern. I'm not sure what happens if there is some complex cyclic
dependency introduced now that parsing order is significant.
In my case specifically, I use namespaces to arbitrarily extend functionality
for client code as long as the user declares new methods in scope of the type
they are compiling. (e.g. serializing a complex, user-defined type).
Does the standard require this behavior, or is this an optimization that has
broken code that does not have gcc-4 correct ordering of its headers?
--
Summary: template fails to resolve names even when the name is in
scope before the call point
Product: gcc
Version: 4.1.2
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
AssignedTo: unassigned at gcc dot gnu dot org
ReportedBy: logicle at live dot com
GCC build triplet: linux 2.6.23 x86_64
GCC host triplet: linux 2.6.23 x86_64
GCC target triplet: linux 2.6.23 x86_64
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34322