From 1390c3d43d0f4eb98be65176cafbf38c1fcbe2b0 Mon Sep 17 00:00:00 2001 From: Jeffrey Yasskin Date: Tue, 15 Jun 2010 23:50:08 +0000 Subject: [PATCH] Describe a gcc compatibility problem that occurs when a template calls a function defined between its declaration and an instantiation, and that function isn't findable through ADL. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@106068 91177308-0d34-0410-b5e6-96231b3b80d8 --- www/content.css | 3 + www/cxx_compatibility.html | 124 +++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) diff --git a/www/content.css b/www/content.css index dca6a32914..574b98e12b 100644 --- a/www/content.css +++ b/www/content.css @@ -23,5 +23,8 @@ IMG.img_slide { .itemTitle { color:#2d58b7 } +span.error { color:red } +span.caret { color:green; font-weight:bold } + /* Tables */ tr { vertical-align:top } diff --git a/www/cxx_compatibility.html b/www/cxx_compatibility.html index 02dabbe8e3..27defc2d8a 100644 --- a/www/cxx_compatibility.html +++ b/www/cxx_compatibility.html @@ -25,6 +25,7 @@
  • Initialization of non-integral static const data members within a class definition
  • Unqualified lookup in templates
  • Unqualified lookup into dependent bases of class templates
  • +
  • Template uses of a function must either find the function by ADL or come after the declaration of the function
  • Incomplete types in templates
  • Templates with no valid instantiations
  • Default initialization of const variable of a class type requires user-defined default constructor
  • @@ -215,6 +216,129 @@ This works whether the methods are static or not, but be careful: if DoThis is virtual, calling it this way will bypass virtual dispatch! + +

    Template uses of a function must either find the function by ADL or come after the declaration of the function

    + + +

    For example, gcc-4.4 accepts the following code:

    + +
    +#include <iostream>
    +#include <utility>
    +#include <vector>
    +
    +template<typename T>
    +void Dump(const T& value) {
    +  std::cout << value << "\n";
    +}
    +
    +template<typename T, typename U>
    +std::ostream& operator<<(std::ostream& out, const std::pair<T, U>& i) {
    +  return out << '(' << i.first << ", " << i.second << ")";
    +}
    +
    +namespace ns {
    +  struct Data {};
    +}
    +
    +std::ostream& operator<<(std::ostream& out, ns::Data) {
    +  return out << "Some data";
    +}
    +
    +void Use() {
    +  Dump(std::make_pair(3, 4.5));
    +  Dump(ns::Data());
    +  Dump(std::vector<const char*>(1, "Hello World"));
    +}
    +
    +template<typename T>
    +std::ostream& operator<<(std::ostream& out, const std::vector<T>& vec) {
    +  out << '[';
    +  for (size_t i = 0, size = vec.size(); i != size; ++i) {
    +    if (i != 0)
    +      out << ", ";
    +    out << vec[i];
    +  }
    +  return out << ']';
    +}
    +
    + +

    while clang, following the rules in [temp.dep.candidate] +complains:

    + +
    +test.cc:7:13: error: invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'std::pair<int, double> const')
    +  std::cout << value << "\n";
    +  ~~~~~~~~~ ^  ~~~~~
    +test.cc:24:3: note: in instantiation of function template specialization 'Dump<std::pair<int, double> >' requested here
    +  Dump(std::make_pair(3, 4.5));
    +  ^
    +test.cc:7:13: error: invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'ns::Data const')
    +  std::cout << value << "\n";
    +  ~~~~~~~~~ ^  ~~~~~
    +test.cc:25:3: note: in instantiation of function template specialization 'Dump<ns::Data>' requested here
    +  Dump(ns::Data());
    +  ^
    +test.cc:7:13: error: invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'std::vector<char const *, std::allocator<char const *> > const')
    +  std::cout << value << "\n";
    +  ~~~~~~~~~ ^  ~~~~~
    +test.cc:26:3: note: in instantiation of function template specialization 'Dump<std::vector<char const *, std::allocator<char const *> > >' requested here
    +  Dump(std::vector<const char*>(1, "Hello World"));
    +  ^
    +3 errors generated.
    +
    + +

    The fix is to

    +
    1. Add a declaration before the use of the function, or +
    2. Move the definition to before the use of the function, or +
    3. Move the function into the same namespace as one of its +arguments. (Note that it still needs to be declared before the +template is instantiated.) +
    + +
    +#include <iostream>
    +#include <utility>
    +#include <vector>
    +
    +template<typename T>  // Fix 1.
    +std::ostream& operator<<(std::ostream& out, const std::vector<T>& vec);
    +
    +template<typename T, typename U>  // Fix 2.
    +std::ostream& operator<<(std::ostream& out, const std::pair<T, U>& i) {
    +  return out << '(' << i.first << ", " << i.second << ")";
    +}
    +
    +template<typename T>
    +void Dump(const T& value) {
    +  std::cout << value << "\n";
    +}
    +
    +namespace ns {
    +  struct Data {};
    +  std::ostream& operator<<(std::ostream& out, Data) {  // Fix 3.
    +    return out << "Some data";
    +  }
    +}
    +
    +void Use() {
    +  Dump(std::make_pair(3, 4.5));
    +  Dump(ns::Data());
    +  Dump(std::vector<const char*>(1, "Hello World"));
    +}
    +
    +template<typename T>
    +std::ostream& operator<<(std::ostream& out, const std::vector<T>& vec) {
    +  out << '[';
    +  for (size_t i = 0, size = vec.size(); i != size; ++i) {
    +    if (i != 0)
    +      out << ", ";
    +    out << vec[i];
    +  }
    +  return out << ']';
    +}
    +
    +

    Incomplete types in templates

    -- 2.40.0