Reputation: 9124
Ben Deane mentions the throw trick, which ensures a link error when a constexpr function is used in a non-constexpr context.
Here is my take:
#include <iostream>
struct Exc;
constexpr int foo( int a )
{
if( a == 42 )
{
throw Exc{};
}
return 666;
}
int main()
{
constexpr auto ret = foo(43);
std::cout << ret << "\n";
return ret;
}
But I couldn't make it work (clang++ 3.8.1-23 and g++ 6.3.0):
$ clang++ -std=c++14 main.cpp
main.cpp:9:15: error: invalid use of incomplete type 'ExcBase'
throw ExcBase{};
^~~~~~~~~
main.cpp:3:8: note: forward declaration of 'ExcBase'
struct ExcBase;
^
1 error generated.
A comment in this thread suggests another trick:
#include <iostream>
constexpr int foo( int a )
{
if( a == 42 )
{
(void)reinterpret_cast<int>(a);
}
return 666;
}
int main()
{
constexpr auto ret = foo(43);
std::cout << ret << "\n";
return ret;
}
Which works: no errors or warnings; when the invocation is replaced with foo(42)
, clang returns:
$ clang++ -std=c++14 main.cpp
main.cpp:19:20: error: constexpr variable 'ret' must be initialized by a constant expression
constexpr auto ret = foo(42);
^ ~~~~~~~
main.cpp:10:15: note: reinterpret_cast is not allowed in a constant expression
(void)reinterpret_cast<int>(a);
^
main.cpp:19:26: note: in call to 'foo(42)'
constexpr auto ret = foo(42);
^
1 error generated.
Which is great. But gcc compiles the code happily in both cases. And now the question. How can a constexpr function be written in such a way, that it cannot be used in non-constexpr environments?
Upvotes: 2
Views: 293
Reputation: 50026
The problem is you actually instantiate (call constructor) a struct which is of incomplete type. This trick you talk about requires any symbol which will not be found at link time. So instead of struct
you may use int
:
http://coliru.stacked-crooked.com/a/3df5207827c8888c
#include <iostream>
extern int Exc;
constexpr int foo( int a )
{
if( a == 42 )
{
throw Exc;
}
return 666;
}
int main()
{
// Compiles
constexpr auto ret = foo(43);
std::cout << ret << "\n";
// This will show linker error as expected:
// /tmp/ccQfT6hd.o: In function `main':
// main.cpp:(.text.startup+0x4c): undefined reference to `Exc'
// collect2: error: ld returned 1 exit status
int nn;
std::cin >> nn;
auto ret2 = foo(nn);
return ret;
}
Upvotes: 5