Search code examples
c++googletesttemplate-argument-deduction

How does including gtest.h break template argument deduction for a std algorithm?


I upgraded to the latest release of Google Test, and several of my tests no longer compiled. I've reduced it to this:

#include <gtest/gtest.h>

#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>

int main () {
    const std::string foo = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    const auto uppers = std::count_if(foo.begin(), foo.end(), std::isupper);
    std::cout << "There are " << uppers << " capitals." << std::endl;
    return 0;
}

The Visual Studio 2019 (16.10.4) compiler with /std:c++latest complains:

1>Source.cpp(10,30): error C2672: 'std::count_if': no matching overloaded function found
1>Source.cpp(10,75): error C2780: 'conditional_t<std::_Is_from_primary<std::iterator_traits<remove_cv<remove_reference<_Ty2>::type>::type>>,std::incrementable_traits<remove_cv<remove_reference<_Ty2>::type>::type>,std::iterator_traits<remove_cv<remove_reference<_Ty2>::type>::type>>::difference_type std::count_if(_ExPo &&,const _FwdIt,const _FwdIt,_Pr) noexcept': expects 4 arguments - 3 provided
1>algorithm(570): message : see declaration of 'std::count_if'
1>Source.cpp(10,30): error C2783: 'conditional_t<std::_Is_from_primary<std::iterator_traits<remove_cv<remove_reference<_Ty>::type>::type>>,std::incrementable_traits<remove_cv<remove_reference<_Ty>::type>::type>,std::iterator_traits<remove_cv<remove_reference<_Ty>::type>::type>>::difference_type std::count_if(_InIt,_InIt,_Pr)': could not deduce template argument for '_Pr'
1>algorithm(553): message : see declaration of 'std::count_if'

If I comment out the inclusion of gtest.h, the code builds and executes correctly.

What could gtest.h be doing that messes up template argument deduction for a call that depends only on std-defined types and functions?

[Note, my question is not how to workaround the problem, but to understand the specific underlying cause. I have a workaround: Replace the std::isupper with a lambda.]


Solution

  • It appears that <gtest/gtest.h> is now including <locale>, which introduces

    template< class charT > bool isupper( charT ch, const locale& loc ) 
    

    into the scope. That means that std::isupper now has two possible functions it could point to and without you specifying which one to use, you get an ambiguity which causes template argument deduction to fail.

    If you do go the lambda route to fix this, make sure you cast the input to std::isupper to an unsigned char like

    const auto uppers = std::count_if(foo.begin(), 
                                      foo.end(), 
                                      []()(auto ch){ return std::isupper(static_cast<unsigned char>(ch));} )