Reputation: 423
I have a class that has no default constructor, but the constructor may throw. I was wanting to have a test like:
EXPECT_THROW(MyClass(param), std::runtime_error);
But the compiler, g++
, complains that there is no default constructor for MyClass
. However, the following...
EXPECT_THROW(MyClass foo(param), std::runtime_error);
...works, and the test passes as expected. Why though won't Googletest accept the temporary object?
class MyClass
{
public:
MyClass(std::string const& filename);
//...
};
Interestingly enough, I had refactored my test to not have the filename in a separate variable, and when asked to check I found the following works:
EXPECT_THROW(MyClass("somefilename"), std::runtime_error);
However the following doesn't:
std::string filename("somefilename");
EXPECT_THROW(MyClass(filename), std::runtime_error);
Upvotes: 16
Views: 15527
Reputation: 6010
If you're hit with the "most vexing parse", the cure is often uniform initialization:
EXPECT_THROW(MyClass{param}, std::runtime_error);
(assuming your compiler understands C++11).
Upvotes: 18
Reputation: 724
When dealing with macros, ultimate tool is analyzing expanded macro:
In your case (and for gtest 1.6):
EXPECT_THROW(MyClass(filename), std::runtime_error);
Expands to:
...
bool gtest_caught_expected = false; \
try { \
if (::testing::internal::AlwaysTrue()) { MyClass(filename); }; \
} \
catch (std::runtime_error const&) { \
gtest_caught_expected = true; \
} \
catch (...) { \
gtest_msg.value = \
"Expected: " "MyClass(filename)" " throws an exception of type " \
"std::runtime_error" ".\n Actual: it throws a different type."; \
goto gtest_label_testthrow_88; \
} \
if (!gtest_caught_expected) { \
gtest_msg.value = \
"Expected: " "MyClass(filename)" " throws an exception of type " \
"std::runtime_error" ".\n Actual: it throws nothing."; \
goto gtest_label_testthrow_88; \
} \
...
As you can see, argument to EXPECT_THROW
is not object, but expression to be evaluated further, within GTEST provided valid try/catch block.
So anything you pass to it, must be able to evaluate as expression within nested scope of current scope. In your case:
MyClass(filename)
Is ambiguous, but according to Most vexing parse rule, declaration interpretation is preferred, so you end up with:
MyClass filename
So you are creating variable named filename of class MyClass - and hence error about missing constructor.
This mechanism is not triggered if you use literal string:
MyClass("some string")
Becouse following would be invalid (and there is no ambiguity):
MyClass "some string"
Upvotes: 8
Reputation: 243
Can you give more information? I constructed an example which works fine with a class that only has a one argument constructor.
#include <iostream>
#include <stdexcept>
#include "gtest/gtest.h"
class m {
public:
m(std::string a) {std::cout << "one argument constructor" << std::endl;}
};
int main() {
EXPECT_THROW(m("hat"), std::runtime_error);
}
Output:
one argument constructor
gtt.cc:12: Failure
Expected: m("hat") throws an exception of type std::runtime_error.
Actual: it throws nothing.
EDIT
I do not claim to be an expert on the arcane inner-workings of the C/C++ pre-processor, but I think this has to do with the rules followed when evaluating expressions, in particular, in the land of the pre-processor, parentheses are king. When the pre-processor evaluates MyClass(filename)
it first evaluates filename which produces a temporary value which is immediately discarded, it then evaluates MyClass()
. Calling MyClass("filename")
causes the pre-processor to actually copy the literal string into the expression. One way around this problem is to call EXPECT_THROW((MyClass(filename)), std::runtime_error)
, i.e. use a set of enclosing parentheses around your statement.
Upvotes: 2