可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Is there a better way to do the following?
#include <iostream>
template <typename T>
T Bar();
template <>
int Bar<int>() { return 3; }
// Potentially other specialisations
int main()
{
std::cout << Bar<int>() << std::endl; // This should work
std::cout << Bar<float>() << std::endl; // This should fail
}
The problem with this solution is that it fails at (understandably) link time with "undefined reference to float Bar<float>()
" or the like. This can be confusing for other developers as they may suspect an implementation file is not being linked.
I do know another potential solution:
template <typename T>
T Bar() { BOOST_STATIC_ASSERT(sizeof(T) == 0); }
This causes a compiler error when Bar<float>()
is requested, exactly what I want. However, I'm concerned that technically a compiler may reject this just as gcc rejects BOOST_STATIC_ASSERT(false)
because it knows that it will fail regardless of the template parameter, since sizeof(T)
can never be zero.
In summary, I want to know whether:
- There is another way to do this.
- I'm mistaken and
BOOST_STATIC_ASSERT(sizeof(T))
actually can't fail without instantiation.
- The only way is to let this be a linker error as above.
回答1:
BOOST_STATIC_ASSERT(sizeof(T) == 0);
isn't allowed to fail until the template is instantiated, so I would just do that one. You are correct that BOOST_STATIC_ASSERT(false);
triggers each time.
The reason for this has to do with two-phase name lookup. This is, essentially, the following: when a template is compiled, it's compiled twice. The first time a compielr sees a template it compiles everything except the expressions dependent on template parameters, and the second compilation happens once the template parameter is known, compiling the instantiation fully.
This is why BOOST_STATIC_ASSERT(false);
will fail always: nothing here is dependent and the assert is processed immediately, as if the function weren't a template at all. (Note that MSVC does not implement two-phase look-up, so this fails at instantiation, incorrectly.) Contrarily, because T
is dependent (§14.6.2.1), BOOST_STATIC_ASSERT(sizeof(T) == 0);
is dependent, and is not allowed to be checked until the template is instantiated. (Where upon it will always fail.)
If a compiler tries to be thoughtful and fail it ahead of time, it would be non-conforming. You're suppose to be able to rely on this stuff. That said, if fear gets the best of you it's trivial to really make it wait:
BOOST_STATIC_ASSERT(sizeof(typename T::please_use_specializations) == 0);
This is both guaranteed to fail, and impossible for a compiler to correctly "smartly" fail ahead of time.
回答2:
This could work:
template <typename T>
T Bar() {
T::ERROR_invalid_template_argument_;
}
template <>
int Bar<int>() { return 3; }
You could also use the highest size possible if you're afraid of using 0:
static_assert(sizeof(T) == -1, "No specialization");
回答3:
You could do something like the following:
template <typename T>
T Bar()
{ T::unspecialized_method_called; }
This of course assumes, that T doesn't have a member with the given name, so you would have to choose your "error message" accordingly (e.g. by violating naming conventions).
回答4:
use static_assert with c++0x
template <typename T>
void bar(){
static_assert(false, " invalid argument type");
}
this will raise an error when compiling.
For c++ 98/2003, we could try this
template <typename T>
void bar(){
char invalid_arg_[0];
}
array at lest contains one element. So the compiler would complain. But the error message might be useless to show what happened.
The first wouldn't be a choice since it always fails.
回答5:
There's a little caveat if you're using gcc without -pedantic, in which case it is possible to have sizeof(T) == 0
- when T
is a zero-length array.
#include <iostream>
#include "boost/static_assert.hpp"
template <typename T>
void Foo()
{
BOOST_STATIC_ASSERT(sizeof(T) == 0);
std::cout << "Actually, it is possible to instantiate this." << std::endl;
}
int main()
{
Foo<int[0]>();
return 0;
}
In this case you can work around it by using this instead:
BOOST_STATIC_ASSERT(sizeof(T) == sizeof(T) + 1);
It may be better to encapsulate this trick, which can improve readability because it expresses your intent:
#define NEVER_INSTANTIATE(T) BOOST_STATIC_ASSERT(sizeof(T) == sizeof(T) + 1);
As GMan explained, this can't fail without instantiation just like sizeof(T) == 0
. However, the moral of this story should probably instead be always compile with -pedantic instead.