mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-10 01:55:08 +00:00
Fix template ordering compatibility docs. I missed another section that covered
the same thing. llvm-svn: 106076
This commit is contained in:
parent
a6d20f446f
commit
babff2ce56
@ -25,7 +25,6 @@
|
||||
<li><a href="#init_static_const">Initialization of non-integral static const data members within a class definition</a></li>
|
||||
<li><a href="#dep_lookup">Unqualified lookup in templates</a></li>
|
||||
<li><a href="#dep_lookup_bases">Unqualified lookup into dependent bases of class templates</a></li>
|
||||
<li><a href="#declaration_ordering">Template uses of a function must either find the function by ADL or come after the declaration of the function</a></li>
|
||||
<li><a href="#undep_incomplete">Incomplete types in templates</a></li>
|
||||
<li><a href="#bad_templates">Templates with no valid instantiations</a></li>
|
||||
<li><a href="#default_init_const">Default initialization of const variable of a class type requires user-defined default constructor</a></li>
|
||||
@ -118,33 +117,103 @@ Note that the forthcoming C++0x standard will allow this.
|
||||
<h2 id="dep_lookup">Unqualified lookup in templates</h2>
|
||||
<!-- ======================================================================= -->
|
||||
|
||||
Some versions of GCC accept the following invalid code:
|
||||
<p>Some versions of GCC accept the following invalid code:
|
||||
|
||||
<pre>
|
||||
template <typename T> struct Foo {
|
||||
void Work(T x) {
|
||||
func(x);
|
||||
}
|
||||
};
|
||||
...
|
||||
void func(int x);
|
||||
...
|
||||
template struct Foo<int>; // or anything else that instantiates Foo<int>::Work
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
template<typename T>
|
||||
void Dump(const T& value) {
|
||||
std::cout << value << "\n";
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
std::ostream& operator<<(std::ostream& out, const std::pair<T, U>& p) {
|
||||
return out << '(' << p.first << ", " << p.second << ")";
|
||||
}
|
||||
</pre>
|
||||
|
||||
The standard says that unqualified names like <tt>func</tt> are looked up
|
||||
when the template is defined, not when it's instantiated. Since
|
||||
<tt>void func(int)</tt> was not declared yet when <tt>Foo</tt> was
|
||||
defined, it's not considered. The fix is usually to
|
||||
declare <tt>func</tt> before <tt>Foo</tt>.
|
||||
<p>Clang complains:</p>
|
||||
|
||||
<pre>
|
||||
<b>test.cc:6:13: <span class=error>error:</span> invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'std::pair<int, double> const')</b>
|
||||
std::cout << value << "\n";
|
||||
<span class=caret>~~~~~~~~~ ^ ~~~~~</span>
|
||||
<b>test.cc:18:3: note:</b> in instantiation of function template specialization 'Dump<std::pair<int, double> >' requested here
|
||||
Dump(std::make_pair(3, 4.5));
|
||||
<span class=caret>^</span>
|
||||
<b>test.cc:6:13: <span class=error>error:</span> invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'ns::Data const')</b>
|
||||
std::cout << value << "\n";
|
||||
<span class=caret>~~~~~~~~~ ^ ~~~~~</span>
|
||||
<b>test.cc:19:3: note:</b> in instantiation of function template specialization 'Dump<ns::Data>' requested here
|
||||
Dump(ns::Data());
|
||||
<span class=caret>^</span>
|
||||
2 errors generated.
|
||||
</pre>
|
||||
|
||||
<p>The standard, in [temp.dep.candidate], says that unqualified names
|
||||
like <tt>operator<<</tt> are looked up when the template is
|
||||
defined, not when it's instantiated. Since
|
||||
<tt>operator<<(std::ostream&, const std::pair<>&)</tt>
|
||||
and <tt>operator<<(std::ostream&, ns::Data)</tt> were not
|
||||
declared yet when <tt>Dump</tt> was defined, they're not considered.
|
||||
|
||||
<p>This is complicated by <i>argument-dependent lookup</i> (ADL),
|
||||
which is done when unqualified names are called as functions,
|
||||
like <tt>func(x)</tt> above. The standard says that ADL is performed
|
||||
in both places if any of the arguments are type-dependent, like
|
||||
<tt>x</tt> is in this example. However, ADL does nothing for builtin
|
||||
types like <tt>int</tt>, so the example is still invalid. See
|
||||
[basic.lookup.argdep] for more information.
|
||||
like <tt>operator<<</tt> above. The standard says that ADL is
|
||||
performed in both places if any of the arguments are type-dependent,
|
||||
like <tt>value</tt> and <tt>p</tt> are in this example.
|
||||
|
||||
<p>The fix is usually to</p>
|
||||
<ol><li>Add a declaration before the use of the function,
|
||||
<li>Move the definition to before the use of the function, or
|
||||
<li>Move the function into the same namespace as one of its arguments
|
||||
so that ADL applies. (Note that it still needs to be declared before
|
||||
the template is <i>instantiated</i>, and that ADL doesn't apply to
|
||||
built-in types.)
|
||||
</ol>
|
||||
|
||||
<pre>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
template<typename T, typename U> // Fix 2
|
||||
std::ostream& operator<<(std::ostream& out, const std::pair<T, U>& p) {
|
||||
return out << '(' << p.first << ", " << p.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());
|
||||
}
|
||||
</pre>
|
||||
|
||||
<!-- ======================================================================= -->
|
||||
<h2 id="dep_lookup_bases">Unqualified lookup into dependent bases of class templates</h2>
|
||||
@ -216,129 +285,6 @@ This works whether the methods are static or not, but be careful:
|
||||
if <tt>DoThis</tt> is virtual, calling it this way will bypass virtual
|
||||
dispatch!
|
||||
|
||||
<!-- ======================================================================= -->
|
||||
<h2 id="declaration_ordering">Template uses of a function must either find the function by ADL or come after the declaration of the function</h2>
|
||||
<!-- ======================================================================= -->
|
||||
|
||||
<p>For example, gcc-4.4 accepts the following code:</p>
|
||||
|
||||
<pre>
|
||||
#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 << ']';
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>while clang, following the rules in <tt>[temp.dep.candidate]</tt>
|
||||
complains:</p>
|
||||
|
||||
<pre>
|
||||
<b>test.cc:7:13: <span class=error>error:</span> invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'std::pair<int, double> const')</b>
|
||||
std::cout << value << "\n";
|
||||
<span class=caret>~~~~~~~~~ ^ ~~~~~</span>
|
||||
<b>test.cc:24:3: note:</b> in instantiation of function template specialization 'Dump<std::pair<int, double> >' requested here
|
||||
Dump(std::make_pair(3, 4.5));
|
||||
<span class=caret>^</span>
|
||||
<b>test.cc:7:13: <span class=error>error:</span> invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'ns::Data const')</b>
|
||||
std::cout << value << "\n";
|
||||
<span class=caret>~~~~~~~~~ ^ ~~~~~</span>
|
||||
<b>test.cc:25:3: note:</b> in instantiation of function template specialization 'Dump<ns::Data>' requested here
|
||||
Dump(ns::Data());
|
||||
<span class=caret>^</span>
|
||||
<b>test.cc:7:13: <span class=error>error:</span> invalid operands to binary expression ('ostream' (aka 'basic_ostream<char>') and 'std::vector<char const *, std::allocator<char const *> > const')</b>
|
||||
std::cout << value << "\n";
|
||||
<span class=caret>~~~~~~~~~ ^ ~~~~~</span>
|
||||
<b>test.cc:26:3: note:</b> 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"));
|
||||
<span class=caret>^</span>
|
||||
3 errors generated.
|
||||
</pre>
|
||||
|
||||
<p>The fix is to</p>
|
||||
<ol><li>Add a declaration before the use of the function, or
|
||||
<li>Move the definition to before the use of the function, or
|
||||
<li>Move the function into the same namespace as one of its
|
||||
arguments. (Note that it still needs to be declared before the
|
||||
template is <i>instantiated</i>.)
|
||||
</ol>
|
||||
|
||||
<pre>
|
||||
#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 << ']';
|
||||
}
|
||||
</pre>
|
||||
|
||||
<!-- ======================================================================= -->
|
||||
<h2 id="undep_incomplete">Incomplete types in templates</h2>
|
||||
<!-- ======================================================================= -->
|
||||
|
Loading…
Reference in New Issue
Block a user