Can I use declval to Construct an Unused Return?

194 Views Asked by At

Let's say I have a templatized function that is going to be specialized, so I really don't care about the base implementation. Can I do something like this:

template <typename T>
T dummy() {
    assert(false);
    return declval<T>();
}

When I try to do this in I get a linking error:

unresolved external symbol char const && __cdecl std::declval<char const >(void) (??$declval@$$CBD@std@@YA$$QEBDXZ) referenced in function char const __cdecl dummy<char const>()

Again this function is uncalled, but I do save a pointer to it. I can use return T{} instead, and that compiles, but I need this to work even if there isn't a default constructor for T. Is there a way that I can work around this?

2

There are 2 best solutions below

1
On BEST ANSWER

You can work around the problem by not providing a definition for the function template. Using

template <typename T>
T dummy();

template <>
int dummy() { std::cout << "template <> int dummy()"; return 42;}

int main()
{
    dummy<int>();
    dummy<double>();
    return 0;
}

You will get a linker error because dummy<double>(); does not exist but if you comment it out then the code will compile because there does exist a specialization for int. This means you don't have to worry about returning anything.


You can eve use

template <typename T>
T dummy() = delete;

instead of not providing a definition so instead of getting a linker error you'll get a "nice" compiler error saying you are trying to use a deleted function. This also allows you to write overloads instead of specializations which is preferable as specializations are not considered during overload resolution. That's not really possible in your case since you don't take any parameters but if you do you should consider it.

0
On

Instantiating dummy will odr-use std::declval<T>, which is not permitted by the standard.

Note that it is not a compile error to simply omit the return statement. It simply results in UB if dummy is called. Since you are sure that dummy will never be called, this should not pose any problem for you.

However, perhaps what you want to do is avoid having the compiler issue a warning that control reaches the end of a non-void function. After all, in a non-debug build, the end of the function will be reached if you happen to call dummy, since the assert will disappear. In that case, I would suggest putting after assert:

throw std::logic_error("dummy should not be called");

The compiler should now see that it is impossible for the function to reach the end of its body without returning a value, since it cannot reach the end at all.

This also makes it more likely that the program will actually crash if dummy actually gets called somehow, rather than invoking UB.