Googletest не принимает временный объект в EXPECT_THROW - PullRequest
13 голосов
/ 23 июня 2011

У меня есть класс, у которого нет конструктора по умолчанию, но конструктор может выдать.Я хотел иметь такой тест:

EXPECT_THROW(MyClass(param), std::runtime_error);

Но компилятор, g++, жалуется, что для MyClass нет конструктора по умолчанию.Однако следующее ...

EXPECT_THROW(MyClass foo(param), std::runtime_error);

... работает, и тест проходит, как и ожидалось.Почему Googletest не принимает временный объект?

class MyClass
{
public:
  MyClass(std::string const& filename);
  //...
};

Интересно, что я провела рефакторинг своего теста, чтобы имя файла не содержалось в отдельной переменной, и когда меня попросили проверить, я обнаружила следующие работы:1012 *

EXPECT_THROW(MyClass("somefilename"), std::runtime_error);

Однако следующее не:

std::string filename("somefilename");
EXPECT_THROW(MyClass(filename), std::runtime_error);

Ответы [ 3 ]

10 голосов
/ 13 ноября 2015

Если вы столкнулись с «самым неприятным синтаксическим анализом», лечение часто происходит при равномерной инициализации:

EXPECT_THROW(MyClass{param}, std::runtime_error);

(при условии, что ваш компилятор понимает C ++ 11).

8 голосов
/ 29 декабря 2014

При работе с макросами конечный инструмент анализирует расширенный макрос:

В вашем случае (и для gtest 1.6):

EXPECT_THROW(MyClass(filename), std::runtime_error);

Расширяется до:

...
    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; \
    } \
...

Как видите, аргумент EXPECT_THROW - это не объект, а выражение, которое будет оцениваться в дальнейшем, в рамках GTEST предусмотрен допустимый блок try / catch.

Поэтому все, что вы передаете ему, должно быть в состоянии оценитькак выражение в пределах вложенной области текущей области.В вашем случае:

MyClass(filename)

является неоднозначным, но в соответствии с наиболее досадным правилом синтаксического анализа, интерпретация объявления предпочтительнее, поэтому вы получите:

MyClass filename

Итак, вы создаете переменную с именемимя файла класса MyClass - и, следовательно, ошибка об отсутствующем конструкторе.

Этот механизм не сработает, если вы используете литеральную строку:

MyClass("some string")

, потому что следующее будет недопустимым (и нет двусмысленности):

MyClass "some string"
2 голосов
/ 23 июня 2011

Можете ли вы дать больше информации?Я создал пример, который отлично работает с классом, который имеет конструктор только с одним аргументом.

#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);
}

Вывод:

one argument constructor
gtt.cc:12: Failure
Expected: m("hat") throws an exception of type std::runtime_error.
Actual: it throws nothing.

EDIT Я не претендую набыть экспертом по тайным внутренним работам препроцессора C / C ++, но я думаю, что это связано с правилами, которым следуют при оценке выражений, в частности, в области препроцессора, круглые скобки - король.Когда препроцессор оценивает MyClass(filename), он сначала оценивает имя файла, которое создает временное значение, которое немедленно отбрасывается, затем он оценивает MyClass().Вызов MyClass("filename") заставляет препроцессор фактически копировать буквенную строку в выражение.Одним из способов решения этой проблемы является вызов EXPECT_THROW((MyClass(filename)), std::runtime_error), т. Е. Использование круглых скобок вокруг вашего оператора.

...