I'm trying to disallow references with a class I've got and I'm seeing some strange behavior. I've built a toy example that shows what's happening. If I have this:
template <class T>
struct something {
};
template <class T>
struct something<T&> {
static_assert(false, "reference disallowed with something");
};
int main() {
something<int> a; (void)a;
}
Even though I'm not declaring an instance of something with a reference, it still fails:
> g++ -std=c++11 foo.cc -o foo
foo.cc:7:5: error: static assertion failed: reference disallowed with something
static_assert(false, "reference disallowed with something");
^
If I tweak it so that it has to use the template parameter through a proxy class, then it works:
template <class T>
struct something {
};
template <class T>
struct something<T&> {
template <class TT> struct falsity {
static const bool value = false;
};
static_assert(falsity<T>::value, "reference disallowed with something");
};
int main() {
something<int> a; (void)a;
}
Then it works just fine, is this the expected behavior? I would have thought the the static assert would be a member of the class regardless.
Edit: this is gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1)
Your static_assert does not depend on any template parameters and so the compiler does not have to wait until instantiation of the class template to evaluate the statement. Instead it does so during the first phase of two phase name lookup and your code fails to compile.
In your case you can fix the failure by changing the assertion to
static_assert(!std::is_lvalue_reference<T>::value, "reference disallowed with something");
Related
I have a template class where I would like to remove a member function if the type satisfies some condition, that, as far as I understand, should be a very basic usage of SFINAE, for example:
template<class T>
class A
{
public:
template<typename = typename std::enable_if<std::is_floating_point<T>::value>::type>
T foo () {
return 1.23;
}
};
However, this is results in an error "no type named 'type'", like SFINAE was not going on. This however works if foo is a function not member of a class. What is wrong with this implementation?
You're missing a dependent name for the compiler to use for SFINAE. Try something like this instead:
#include <type_traits>
template<class T>
class A
{
public:
template<typename Tp = T>
typename std::enable_if<std::is_floating_point<Tp>::value, Tp>::type
foo () {
return 1.23;
}
};
int main() {
A<double> a;
a.foo();
}
If the type T is not floating point, the declaration would be malformed (no return type) and the function would not be considered for the overload set.
See it on godbolt.
Consider following code:
template <typename T> struct X
{
X(T) {}
void foo() {}
};
template <typename T> struct Y
{
int object = 0;
void bar()
{
X(object).foo();
}
};
Live on gcc.godbold.org
GCC 8.2 compiles it, while Clang 7 spits out following error:
<source>:13:18: error: member reference base type 'X' is not a structure or union
X(object).foo();
~~~~~~~~~^~~~
This looks like a bug to me.
The conditions are very specific: If either structure is not a template, or if object is not a member variable, or if CTAD (class template argument deduction) is not involved, then Clang compiles the code as well.
What's going on here? Is it indeed a Clang bug?
And what's more important, how can I make the code compile with minimal changes, preferrably without getting rid of CTAD?
The only flag used is -std=c++17.
clang++ --version is
clang version 7.0.0 (tags/RELEASE_700/final 342594)
Target: x86_64-unknown-linux-gnu
Thread model: posix
Yes, this is clang bug see Class template argument deduction with deduced type fails when accessing member which says:
Try to compile the following c++ program:
template <class T>
struct C {
C(T) {}
int a;
};
template <class T>
int foo(T v) {
return C{v}.a; // <----
}
int main() {
foo(4);
}
The line marked above fails with the error:
error: member reference base type 'C' is not a structure or union
return (C{v}).a;
~~~~~~^~
The bug report also specifies cases that do work, which may or may not be alternatives.
Note that the following all work fine:
template <class T>
C<T> foo(T v) {
return C{v};
}
and
int foo(int v) {
return C{v}.a;
}
and
C{4}.a;
I tried this also on a recent trunk build (trunk 346600)
I wanted to check that the extern keyword did infact prevent class code being generated in the translation unit:
template<class> struct always_false : std::false_type {};
template <typename T> class A{
static_assert(always_false<T>::value, "If this fires, 'A' is instantiated");
};
extern template class A<int>; //error: static assertion failed: If this fires, 'A' is instantiated|
int main(){
// A<int> f;
}
Why is it that the previous code still produces an error from the static_assert if this is my only source file? As far as I understand from the explicit use of extern this should prevent any production of code for the class A<int> and the linker takes care of finding a later explicit instantiation definition (in the translation unit for which the code is actually written) to match any use of A<int> with.
However it seems that the explicit instantiation declaration is generating code in this translation unit itself as indicated by the compilation error. If I comment out extern template class A<int> everything works fine. I was using GCC 4.9.2. but it appears clang 3.5.1 throws this error too.
Alternatively this also kicks up the same assert error:
template<class> struct always_false : std::false_type {};
template <typename T> class A{
public:
void test() { static_assert(always_false<T>::value, "If this fires, 'test()' is instantiated"); }
};
extern template void A<int>::test();
int main(){
A<int> a;
a.test();
}
Here I would've expected the member function A<int>::test() to not even be instantiated and again wait until linking before "finding" code for the function, but it looks like the code is generated in the same translation unit. However if I take out the static_assert:
template <typename T> class A{
public:
void test() { }
};
extern template void A<int>::test();
int main(){
A<int> a;
a.test();
}
Then I get the error I'm expecting, indicating that A<int>::test() is not instantiated and there was a linker error:
**undefined reference to `A<int>::test()'|**
Why would the static_assert throw an error if test() was never instantiated?
Your premise is wrong. extern template prevents object code generation for function templates (including member functions of class templates), but it doesn't prevent the instantiation of the class bodies.
Edit: To answer the updated question: the member function is defined inline in the class, so the compiler will still instantiate it so that it can inline it if necessary. If you define the function out of line, you will not get an error (tried GCC 5.2.0 via godbolt).
#include <type_traits>
template<class> struct always_false : std::false_type {};
template <typename T> class A{
public:
void test();
};
template <typename T>
void A<T>::test() { static_assert(always_false<T>::value, "If this fires, 'test()' is instantiated"); }
extern template void A<int>::test();
int main(){
A<int> a;
a.test();
}
With variable templates coming in C++14 (and Clang already supporting them) and a proposal for standard is_same_v and likewise type traits, I figured being able to make new type traits as follows would be neat:
template<typename T>
constexpr bool is_const_and_volatile{std::is_const_v<T> && std::is_volatile_v<T>};
Alas, this results in errors equivalent to the following SSCCE (this one contains everything mentioned below):
#include <type_traits>
template<typename T>
constexpr bool is_pointer{std::is_pointer<T>::value};
template<typename T>
constexpr bool foo{is_pointer<T>};
int main() {
//foo<int *>;
}
With the line in main commented, Clang spits out the following:
warning: variable is_pointer<type-parameter-0-0> has internal linkage but is not defined
It looks defined to me (note that changing T to int * in foo works fine). Uncommenting the line in main to instantiate foo gives this (again, T to int * works fine):
error: constexpr variable foo<int *> must be initialized by a constant expression
However, replacing foo with the following old syntax causes both instances to work fine:
constexpr bool foo{std::is_pointer<T>::value};
Is there something I'm missing about variable templates? Is there a way to build new variable templates with them, or am I forced to use the older syntax to build new ones and only enjoy the syntactic sugar when using them for other code?
Your code is valid, and is accepted by clang SVN. The link error was caused by clang bug 17846, which I fixed a couple of days ago.
The following seems to work:
#include <type_traits>
#include <iostream>
template<typename T>
struct test {
static constexpr bool is_pointer{std::is_pointer<T>::value};
};
template<typename T>
constexpr bool test<T>::is_pointer;
template<typename T>
constexpr bool foo{test<T>::is_pointer};
int main() {
std::cout << foo<bool>;
std::cout << foo<bool*>;
}
Live Example
Although it procs the same warning if used in a constexpr context so I suppose it doesn't really work after all.
// Fail
template<typename T>
typename std::enable_if<foo<T>, void>::type bar()
{
}
int main() {
bar<bool*>();
}
main.cpp:21:5: error: no matching function for call to 'bar'
bar<bool*>();
^~~~~~~~~~
main.cpp:16:45: note: candidate template ignored: substitution failure [with T = bool *]: non-type template argument is not a constant expression
typename std::enable_if<foo<T>, void>::type bar()
It does stop complaining if you give foo an explicit type:
template<typename T>
typename std::enable_if<foo<bool*>, void>::type bar()
{
}
Or just use test<T>::is_pointer directly:
template<typename T>
typename std::enable_if<test<T>::is_pointer, void>::type bar()
{
}
I'm trying to compile some Microsoft Visual C++ code using g++. Now I've come across a compiler error which I really can't understand. The (simplified) code looks like this:
template<int X> struct A {
template<class Ret> static Ret call() {
return 0;
}
};
template<int X> struct B : A<X> {
int f() {
return A<X>::call<int>();
}
};
When I try to compile this with g++ (version 4.4.5), I get the following error:
main.cpp: In member function int B<X>::f():
main.cpp:16: error: expected primary-expression before int
main.cpp:16: error: expected ; before int
main.cpp:16: error: expected unqualified-id before > token
If I remove the template type (Ret) from method A::call, the code compiles just fine. Can anybody see whats wrong here?
Thanks!
You need the template keyword:
return A<X>::template call<int>();
call is a dependent name, meaning that its signification depends on a template parameter, which is not known when the compiler process f(). You need to indicate that call is a function template by prefixing it with the template keyword.
The same thing happens when you try to access a nested type: you need to add the typename keyword to indicate that the name denotes a type:
template <typename T>
struct A { typedef int type; };
template <typename T>
void f()
{
typename A<T>::type i = 0; // notice "typename" here
}
And sometimes you even need to mix both:
template <typename T>
struct A
{
template <typename U>
struct inner { };
};
template <typename T>
void f()
{
typename A<T>::template inner<int> var;
}
The use of these two keywords is thoroughly explained in the answers to this question: Where and why do I have to put the “template” and “typename” keywords? (thanks to #Björn Pollex for finding the link).
A is a template, and without knowing X the compiler can't determine the contents of A<X>. Especially it doesn't know that call will end up being a template.
To tell that to the compiler you have to use the template keyword:
template<int X> struct B : A<X> {
int f() {
return A<X>::template call<int>();
}
};
You have to specify that the function you're calling is a template, as it's part of a template class. The compiler is not aware that any given A<X> has a template function named call, and therefore you need to help it.
template<int X> struct B : A<X> {
int f() {
return A<X>::template call<int>();
}
};