Why can't I specialize std::tuple_element? - c++11

The following program attempts to provide a specialization for std::tuple_element for the user-defined type foo. Unfortunately, clang-3.5 rejects it with libc++, but using other compilers or using other standard libraries with clang-3.5 accept the program. Is it correct? If no, why not?
#include <utility>
struct foo {};
namespace std
{
template<size_t, class> struct tuple_element;
template<size_t i>
struct tuple_element<i, foo> {};
}
int main()
{
return 0;
}
Compiler output:
$ clang-3.5 -std=c++11 -stdlib=libc++ -lc++ test.cpp
test.cpp:11:8: error: explicit specialization of non-template struct 'tuple_element'
struct tuple_element<i, foo> {};
^ ~~~~~~~~
1 error generated.
$ clang-3.5 -std=c++11 -lstdc++ test.cpp
(no error)
$ g++-4.9 -std=c++11 test.cpp
(no error)

libc++'s entities are actually in std::__1::, an inline namespace inside std. So your own forward declaration template<size_t, class> struct tuple_element; actually declares a different class template, and then the partial specialization blows up due to ambiguity (though the error message is misleading).
Remove the forward declaration and it should work.

Related

Can't compile using boost::bimap [duplicate]

With gcc 10.1 and boost 1.73.0, the following code
#include <boost/bimap.hpp>
int main() {
boost::bimap<int, int> lookup;
}
fails to compile with flags -O2 --std=c++20, but will succeed with flags -O2 -std=c++17 (verified with compiler explorer).
This is possibly related to the following issue: https://github.com/boostorg/bimap/pull/15 (deprecated std::allocator<void>)
Is there some workaround I can use for now to get this code to successfully compile with --std=c++20?
The reason behind the error is not that the std::allocator<void> specialization is removed. Actually, it's still valid as the primary template with the void argument. The code fails to compile, because ::rebind is no longer part of std::allocator itself. Instead, an implementation should use std::allocator_traits<Alloc>::rebind_alloc<U>.
Fortunately, most containers usually allow to specify a custom allocator as a template argument. It's also the case for boost::bimap, where an allocator can be the third, fourth or fifth template parameter, according to docs. It defaults to std::allocator, but instead, boost::container::allocator can be used, which does not produce the error:
#include <boost/bimap.hpp>
#include <boost/container/allocator.hpp>
int main() {
boost::bimap<int, int, boost::container::allocator<int>> lookup;
}
This issue has recently been fixed in 6fba6e5. boost::bimap now properly detects if a nested ::rebind is available:
template<class A, class T, class = void>
struct allocator_rebind {
typedef typename detail::alloc_to<A, T>::type type;
};
template<class A, class T>
struct allocator_rebind<A, T,
typename detail::alloc_void<typename A::template rebind<T>::other>::type> {
typedef typename A::template rebind<T>::other type;
};

Compiler error in consecutive calls to constexpr functions

I've got the following example:
#include <cstdint>
template<typename T>
struct a_size {
constexpr static std::size_t size() { return sizeof(T); }
};
template<typename T>
constexpr std::size_t foo(T const& t) {
constexpr auto result = a_size<decltype(t)>::size();
return result;
}
template<typename T>
constexpr std::size_t bar(T const& t) {
constexpr auto result = foo(t);
return result;
}
int main() {
constexpr auto r = bar('c');
return r;
}
When compiling with g++ (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0 everything seems to be fine. But when I compile with clang++ (Version(s): clang version 10.0.0 (https://github.com/llvm-mirror/clang.git df2d9221d77042d6b07a6025edf9e810f1801ef5) (https://github.com/llvm-mirror/llvm.git a1e2fd38b595bd636661ac7416961e029c7e1689)) I've got the following error:
main.cc:16:33: error: constexpr variable 'result' must be initialized by a constant expression
constexpr auto result = foo(t);
~~~~^~
main.cc:21:24: note: in instantiation of function template specialization 'bar<char>' requested here
constexpr auto r = bar('c');
^
1 error generated.
I observe the similar behaviour on godbolt.org with clang 8.0.0 and clang trunk, the example is working, however, with GCC both 9.2.0 and trunk.
Compiled as
clang++ -std=c++17 main.cc -o main.run
g++ -std=c++17 main.cc -o main.run
Questions
Why is it so?
Why isn't constexpr-essness transitively applied/used (for lack of a better word) throughout the whole call-chain? (I understand that the result of the call to foo(t) is for some reason not marked constexpr. The question is about why.)
Which compiler is right?
What does the standard say?
Is it a bug? And if so, whether is it in GCC/G++ or in Clang?
Why removing a reference (&) in bar(T const&) solves the problem and clang no longer produces compilation error?
Edit (16.09.2019)
Obviously removing the intermediary result variable solves the problem (as suggested by David Ledger). But this does not answer the other questions. Not to mention that some use cases may require such an intermediary variable.

Fixing warning "Wundefined-var-template"

Searching for duplicates currently gives:
This post which specifically treats the case of a Singleton implementation, and in which the answers avoids the warning altogether with a different implementation.
This post which answers itself without solving the issue.
A suggested duplicate which explains how to implement template member functions (not relevant).
Another suggested duplicate explaining how to define template static members (not relevant).
As far as I understand, none of these answers the question of how to get rid of Wundefined-var-template with clang++ 3.8+ in a situation similar to the MCVE below?
File a.h
#ifndef A_INCLUDED
#define A_INCLUDED
template <class T>
struct A
{
static const char *name;
};
#endif
File a.cpp
#include "a.h"
template <> const char* A<double>::name = "Johnny";
template <> const char* A<float>::name = "Dude";
File b.cpp
#include <cstdio>
#include "a.h"
void say_it() {
printf( "%s\n", A<double>::name );
}
Run from a terminal:
$ clang++ -c -o a.o -std=c++11 a.cpp
$ clang++ -c -o b.o -std=c++11 b.cpp a.o
clang: warning: a.o: 'linker' input unused [-Wunused-command-line-argument]
b.cpp:5:32: warning: instantiation of variable 'A<double>::name' required here, but no definition is available [-Wundefined-var-template]
printf( "%s\n", A<double>::name );
^
./a.h:7:28: note: forward declaration of template entity is here
static const char *name;
^
b.cpp:5:32: note: add an explicit instantiation declaration to suppress this warning if 'A<double>::name' is explicitly instantiated in another translation unit
printf( "%s\n", A<double>::name );
^
1 warning generated.
Demo
Do as warning message explain, add (in a.h):
template <> const char* A<double>::name;
Demo

why does g++ handle namespaces for definitions with 'using' differently than typedefs [duplicate]

Background
Everybody agrees that
using <typedef-name> = <type>;
is equivalent to
typedef <type> <typedef-name>;
and that the former is to be preferred to the latter for various reasons (see Scott Meyers, Effective Modern C++ and various related questions on stackoverflow).
This is backed by [dcl.typedef]:
A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword
becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that
typedef-name. Such a typedef-name has the same semantics as if it were introduced by the typedef specifier.
However, consider a declaration such as
typedef struct {
int val;
} A;
For this case, [dcl.typedef] specifies:
If the typedef declaration defines an unnamed class (or enum), the first typedef-name declared by the
declaration to be that class type (or enum type) is used to denote the class type (or enum type) for linkage
purposes only (3.5).
The referenced section 3.5 [basic.link] says
A name having namespace scope that has not
been given internal linkage above has the same linkage as the enclosing namespace if it is the name of
[...]
an unnamed class defined in a typedef declaration in which the class has
the typedef name for linkage purposes [...]
Assuming the typedef declaration above is done in the global namespace, the struct A would have external linkage, since the global namespace has external linkage.
Question
The question is now whether the same is true, if the typedef declaration is replaced by an alias declaration according to the common notion that they are equivalent:
using A = struct {
int val;
};
In particular, does the type A declared via the alias declaration ("using") have the same linkage as the one declared via the typedef declaration?
Note that [decl.typedef] does not say that an alias declaration is a typedef declaration (it only says that both introduce a typedef-name) and that [decl.typedef] speaks only of a typedef declaration (not an alias declaration) having the property of introducing a typedef name for linkage purposes.
If the alias declaration is not capable of introducing a typedef name for linkage purposes, A would just be an alias for an anonymous type and have no linkage at all.
IMO, that's at least one possible, albeit strict, interpretation of the standard. Of course, I may be overlooking something.
This raises the subsequent questions:
If there is indeed this subtle difference, is it by intention or is
it an oversight in the standard?
What is the expected behavior of compilers/linkers?
Research
The following minimal program consisting of three files (we need at least two separate compilation units) is used to investigate the issue.
a.hpp
#ifndef A_HPP
#define A_HPP
#include <iosfwd>
#if USING_VS_TYPEDEF
using A = struct {
int val;
};
#else
typedef struct {
int val;
} A;
#endif
void print(std::ostream& os, A const& a);
#endif // A_HPP
a.cpp
#include "a.hpp"
#include <iostream>
void print(std::ostream& os, A const& a)
{
os << a.val << "\n";
}
main.cpp
#include "a.hpp"
#include <iostream>
int main()
{
A a;
a.val = 42;
print(std::cout, a);
}
GCC
Compiling this with gcc 7.2 with the "typedef" variant compiles cleanly and provides the expected output:
> g++ -Wall -Wextra -pedantic-errors -DUSING_VS_TYPEDEF=0 a.cpp main.cpp
> ./a.out
42
The compilation with the "using" variant produces a compile error:
> g++ -Wall -Wextra -pedantic-errors -DUSING_VS_TYPEDEF=1 a.cpp main.cpp
a.cpp:4:6: warning: ‘void print(std::ostream&, const A&)’ defined but not used [-Wunused-function]
void print(std::ostream& os, A const& a)
^~~~~
In file included from main.cpp:1:0:
a.hpp:16:6: error: ‘void print(std::ostream&, const A&)’, declared using unnamed type, is used but never defined [-fpermissive]
void print(std::ostream& os, A const& a);
^~~~~
a.hpp:9:2: note: ‘using A = struct<unnamed>’ does not refer to the unqualified type, so it is not used for linkage
};
^
a.hpp:16:6: error: ‘void print(std::ostream&, const A&)’ used but never defined
void print(std::ostream& os, A const& a);
^~~~~
This looks like GCC follows the strict interpretation of the standard above and makes a difference concerning linkage between the typedef and the alias declaration.
Clang
Using clang 6, both variants compile and run cleanly without any warnings:
> clang++ -Wall -Wextra -pedantic-errors -DUSING_VS_TYPEDEF=0 a.cpp main.cpp
> ./a.out
42
> clang++ -Wall -Wextra -pedantic-errors -DUSING_VS_TYPEDEF=1 a.cpp main.cpp
> ./a.out
42
One could therefore also ask
Which compiler is correct?
This looks to me like a bug in GCC.
Note that [decl.typedef] does not say that an alias declaration is a typedef declaration
You're right, [dcl.dcl]p9 gives a definition of the term typedef declaration which excludes alias-declarations. However, [dcl.typedef] does explicitly say, as you quoted in your question:
2 A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that typedef-name. It has the same semantics as if it were introduced by the typedef specifier. [...]
"The same semantics" doesn't leave any doubt. Under GCC's interpretation, typedef and using have different semantics, therefore the only reasonable conclusion is that GCC's interpretation is wrong. Any rules applying to typedef declarations must be interpreted as applying to alias-declarations as well.
It looks like the standard is unclear on this.
On one hand,
[dcl.typedef] A typedef-name can also be introduced by an alias-declaration. [...] Such a typedef-name has the same semantics as if it were introduced by the typedef specifier.
On the other hand, the standard clearly separates the notions of typedef declaration and alias-declaration (the latter term is a grammar production name, so it is italicised and hyphenated; the former is not). In some contexts it talks about "a typedef declaration or alias-declaration", making them equivalent in these contexts; and sometimes it talks solely about "a typedef declaration". In particular, whenever the standard talks about linkage and typedef declarations, it only talks about typedef declarations and does not mention alias-declaration. This includes the key passage
[dcl.typedef] If the typedef declaration defines an unnamed class (or enum), the first typedef-name declared by the declaration to be that class type (or enum type) is used to denote the class type (or enum type) for linkage
purposes only.
Note the standard insists on the first typedef-name being used for linkage. This means that in
typedef struct { int x; } A, B;
only A is used for linkage, and B is not. Nothing in the standard indicates that a name introduced by alias-declaration should behave like A and not like B.
It is my opinion that the standard is insufficiently clear in this area. If the intent is to make only typedef declaration work for linkage, then it would be appropriate to state explicitly in [dcl.typedef] that alias-declaration does not. If the intent is to make alias-declaration work for linkage, this should be stated explicitly too, as is done in other contexts.

using vs. typedef - is there a subtle, lesser known difference?

Background
Everybody agrees that
using <typedef-name> = <type>;
is equivalent to
typedef <type> <typedef-name>;
and that the former is to be preferred to the latter for various reasons (see Scott Meyers, Effective Modern C++ and various related questions on stackoverflow).
This is backed by [dcl.typedef]:
A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword
becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that
typedef-name. Such a typedef-name has the same semantics as if it were introduced by the typedef specifier.
However, consider a declaration such as
typedef struct {
int val;
} A;
For this case, [dcl.typedef] specifies:
If the typedef declaration defines an unnamed class (or enum), the first typedef-name declared by the
declaration to be that class type (or enum type) is used to denote the class type (or enum type) for linkage
purposes only (3.5).
The referenced section 3.5 [basic.link] says
A name having namespace scope that has not
been given internal linkage above has the same linkage as the enclosing namespace if it is the name of
[...]
an unnamed class defined in a typedef declaration in which the class has
the typedef name for linkage purposes [...]
Assuming the typedef declaration above is done in the global namespace, the struct A would have external linkage, since the global namespace has external linkage.
Question
The question is now whether the same is true, if the typedef declaration is replaced by an alias declaration according to the common notion that they are equivalent:
using A = struct {
int val;
};
In particular, does the type A declared via the alias declaration ("using") have the same linkage as the one declared via the typedef declaration?
Note that [decl.typedef] does not say that an alias declaration is a typedef declaration (it only says that both introduce a typedef-name) and that [decl.typedef] speaks only of a typedef declaration (not an alias declaration) having the property of introducing a typedef name for linkage purposes.
If the alias declaration is not capable of introducing a typedef name for linkage purposes, A would just be an alias for an anonymous type and have no linkage at all.
IMO, that's at least one possible, albeit strict, interpretation of the standard. Of course, I may be overlooking something.
This raises the subsequent questions:
If there is indeed this subtle difference, is it by intention or is
it an oversight in the standard?
What is the expected behavior of compilers/linkers?
Research
The following minimal program consisting of three files (we need at least two separate compilation units) is used to investigate the issue.
a.hpp
#ifndef A_HPP
#define A_HPP
#include <iosfwd>
#if USING_VS_TYPEDEF
using A = struct {
int val;
};
#else
typedef struct {
int val;
} A;
#endif
void print(std::ostream& os, A const& a);
#endif // A_HPP
a.cpp
#include "a.hpp"
#include <iostream>
void print(std::ostream& os, A const& a)
{
os << a.val << "\n";
}
main.cpp
#include "a.hpp"
#include <iostream>
int main()
{
A a;
a.val = 42;
print(std::cout, a);
}
GCC
Compiling this with gcc 7.2 with the "typedef" variant compiles cleanly and provides the expected output:
> g++ -Wall -Wextra -pedantic-errors -DUSING_VS_TYPEDEF=0 a.cpp main.cpp
> ./a.out
42
The compilation with the "using" variant produces a compile error:
> g++ -Wall -Wextra -pedantic-errors -DUSING_VS_TYPEDEF=1 a.cpp main.cpp
a.cpp:4:6: warning: ‘void print(std::ostream&, const A&)’ defined but not used [-Wunused-function]
void print(std::ostream& os, A const& a)
^~~~~
In file included from main.cpp:1:0:
a.hpp:16:6: error: ‘void print(std::ostream&, const A&)’, declared using unnamed type, is used but never defined [-fpermissive]
void print(std::ostream& os, A const& a);
^~~~~
a.hpp:9:2: note: ‘using A = struct<unnamed>’ does not refer to the unqualified type, so it is not used for linkage
};
^
a.hpp:16:6: error: ‘void print(std::ostream&, const A&)’ used but never defined
void print(std::ostream& os, A const& a);
^~~~~
This looks like GCC follows the strict interpretation of the standard above and makes a difference concerning linkage between the typedef and the alias declaration.
Clang
Using clang 6, both variants compile and run cleanly without any warnings:
> clang++ -Wall -Wextra -pedantic-errors -DUSING_VS_TYPEDEF=0 a.cpp main.cpp
> ./a.out
42
> clang++ -Wall -Wextra -pedantic-errors -DUSING_VS_TYPEDEF=1 a.cpp main.cpp
> ./a.out
42
One could therefore also ask
Which compiler is correct?
This looks to me like a bug in GCC.
Note that [decl.typedef] does not say that an alias declaration is a typedef declaration
You're right, [dcl.dcl]p9 gives a definition of the term typedef declaration which excludes alias-declarations. However, [dcl.typedef] does explicitly say, as you quoted in your question:
2 A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that typedef-name. It has the same semantics as if it were introduced by the typedef specifier. [...]
"The same semantics" doesn't leave any doubt. Under GCC's interpretation, typedef and using have different semantics, therefore the only reasonable conclusion is that GCC's interpretation is wrong. Any rules applying to typedef declarations must be interpreted as applying to alias-declarations as well.
It looks like the standard is unclear on this.
On one hand,
[dcl.typedef] A typedef-name can also be introduced by an alias-declaration. [...] Such a typedef-name has the same semantics as if it were introduced by the typedef specifier.
On the other hand, the standard clearly separates the notions of typedef declaration and alias-declaration (the latter term is a grammar production name, so it is italicised and hyphenated; the former is not). In some contexts it talks about "a typedef declaration or alias-declaration", making them equivalent in these contexts; and sometimes it talks solely about "a typedef declaration". In particular, whenever the standard talks about linkage and typedef declarations, it only talks about typedef declarations and does not mention alias-declaration. This includes the key passage
[dcl.typedef] If the typedef declaration defines an unnamed class (or enum), the first typedef-name declared by the declaration to be that class type (or enum type) is used to denote the class type (or enum type) for linkage
purposes only.
Note the standard insists on the first typedef-name being used for linkage. This means that in
typedef struct { int x; } A, B;
only A is used for linkage, and B is not. Nothing in the standard indicates that a name introduced by alias-declaration should behave like A and not like B.
It is my opinion that the standard is insufficiently clear in this area. If the intent is to make only typedef declaration work for linkage, then it would be appropriate to state explicitly in [dcl.typedef] that alias-declaration does not. If the intent is to make alias-declaration work for linkage, this should be stated explicitly too, as is done in other contexts.

Resources